2017-12-10 145 views
6

Tôi đang cố gắng hiểu Cổng kết thúc I/O và cụ thể cách chúng liên quan đến việc sử dụng async - await cho I/O.IOCP là một chuỗi đang chạy trong khi I/O đang diễn ra hay sau?

Bài báo khét tiếng There is No Thread nói về IOCP được mượn một thời gian ngắn sau khi I/O hoàn tất. Bởi vì toàn bộ điểm của bài viết là để cho thấy rằng khi phần cứng cấp I/O ưa thích là trên máy bay, không có luồng nào được tiêu thụ bởi một vòng lặp như

Là I/O chưa ? Số I/O đã được thực hiện chưa? Số I/O đã được thực hiện chưa? Số ...

Nhưng sau đó tôi nhìn vào this article mà nói rằng một

"thành phần có trách nhiệm kiểm tra các cổng kết thúc cho xếp hàng đợi yếu tố"

và đưa ra ví dụ như

public class IOCompletionWorker 
{ 
    public unsafe void Start(IntPtr completionPort) 
    { 
     while (true) 
     { 
      uint bytesRead; 
      uint completionKey; 
      NativeOverlapped* nativeOverlapped; 

      var result = Interop.GetQueuedCompletionStatus(
       completionPort, 
       out bytesRead, 
       out completionKey, 
       &nativeOverlapped, 
       uint.MaxValue); 

      var overlapped = Overlapped.Unpack(nativeOverlapped); 

      if (result) 
      { 
       var asyncResult = ((FileReadAsyncResult)overlapped.AsyncResult); 
       asyncResult.ReadCallback(bytesRead, asyncResult.Buffer); 
      } 
      else 
      { 
       ThreadLogger.Log(Interop.GetLastError().ToString()); 
      } 

      Overlapped.Free(nativeOverlapped); 
     } 
    } 
} 

var completionPortThread = new Thread(() => new IOCompletionWorker().Start(completionPortHandle)) 
{ 
    IsBackground = true 
}; 
completionPortThread.Start(); 

đối với tôi có vẻ như có một số cuộc thăm dò đang diễn ra.

Tôi đoán câu hỏi của tôi đun sôi xuống

  • Có đúng khi nói rằng một ứng dụng .NET có 2 loại hồ thread - (1) "đề người lao động" và (2) "I/O chủ đề "?
  • Nếu đúng, có số cố định, được chỉ định trong cấu hình, như chuỗi công việc M và chuỗi N I/O không? Và tỷ lệ của M đến N là bao nhiêu?
  • Khi nào các chuỗi I/O được sử dụng chính xác?
+0

câu hỏi của bạn có được trả lời không? :) – fbrosseau

Trả lời

5

Cả hai bài viết đều đúng theo cách riêng của chúng.

IOCP không phải là chủ đề. Chúng có thể được xem như một loại hàng đợi trong đó hạt nhân (hoặc mã chế độ người dùng thông thường, thông qua PostQueuedCompletionStatus) có thể đăng các mục hoàn thành. Không có mô hình luồng hoặc chủ đề luồng vốn có liên quan với chính IOCP, chúng chỉ đơn giản là nhiều hàng đợi của nhà sản xuất.

Chúng ta hãy Sockets mạng là một ví dụ, nhưng điều này cũng đúng đối với bất kỳ loại công việc không đồng bộ:

  • Bạn gọi WSARecv của bạn trên ổ cắm chồng chéo chế độ của bạn bị ràng buộc vào một IOCP, nó tùy thuộc vào mạng trình điều khiển để làm bất cứ điều gì là cần thiết để thiết lập các yêu cầu thực tế để tiếp nhận dữ liệu. Không có chủ đề nào đang chờ dữ liệu của bạn đến.
  • Dữ liệu đến. Hệ điều hành được đánh thức bởi phần cứng. Hệ điều hành sẽ cung cấp cho trình điều khiển mạng một số thời gian CPU trong nhân để xử lý sự kiện đến. Trình điều khiển mạng xử lý ngắt và sau đó bởi vì socket của bạn bị ràng buộc với IOCP, hãy đăng một mục hoàn thành vào hàng đợi IOCP của bạn. Yêu cầu hoàn tất.

Không có chuỗi chế độ người dùng thực sự từ quá trình của bạn tham gia vào bất kỳ thao tác nào (ngoài cuộc gọi không đồng bộ ban đầu). Nếu bạn muốn hành động trên thực tế là dữ liệu của bạn đã đến (mà tôi giả sử bạn làm khi bạn đang đọc từ một ổ cắm!), Sau đó bạn phải dequeue các mục hoàn thành từ IOCP của bạn.

Điểm của IOCP là bạn có thể liên kết hàng nghìn xử lý IO (ổ cắm, tệp, ...) với một IOCP duy nhất. Sau đó, bạn có thể sử dụng một chuỗi đơn để thúc đẩy hàng nghìn quy trình không đồng bộ đó song song.

Có, một chủ đề đang thực hiện GetQueuedCompletionStatus bị chặn trong khi không có quá trình hoàn thành đang chờ xử lý trên IOCP, vì vậy đó có thể là nơi mà sự nhầm lẫn của bạn xuất phát. Nhưng điểm của IOCP là bạn chặn một luồng đó trong khi bạn có thể có hàng trăm nghìn hoạt động mạng đang chờ xử lý tại bất kỳ thời điểm nào, tất cả đều được phục vụ bởi một luồng của bạn. Bạn sẽ không bao giờ làm một ánh xạ 1 đến 1 giữa IO handle/IOCP/Servicing Thread, bởi vì sau đó bạn sẽ mất bất kỳ lợi ích nào từ việc không đồng bộ, và bạn cũng có thể sử dụng IO đồng bộ.

Điểm chính của IOCP là đạt được tính song song ấn tượng của các hoạt động không đồng bộ trong Windows.

Tôi hy vọng điều này sẽ làm rõ sự nhầm lẫn.

Đối với các câu hỏi cụ thể

  1. Vâng, .Net framework có hai hồ bơi. Một là hoàn toàn cho công việc mục đích chung chế độ người dùng, các threader "công nhân". Khác là thread "IO". Điều thứ hai là để tất cả quản lý IOCP có thể được ẩn khỏi bạn khi viết mã C# cấp cao và để ổ cắm không đồng bộ của bạn hoạt động giống như ma thuật.
  2. Đây là tất cả chi tiết triển khai có thể thay đổi bất kỳ lúc nào, nhưng câu trả lời là cả hai hồ đều độc lập. Nếu bạn có băng thông công việc lớn xảy ra trên luồng công nhân và khung công tác quyết định rằng tổng thông lượng của bạn sẽ tăng bằng cách thêm chủ đề mới, nó sẽ thêm chủ đề vào nhóm công nhân một mình và không chạm vào nhóm IO. Điều tương tự cũng xảy ra đối với nhóm IO, nếu bạn có mã sai để chặn các luồng IO trong callbacks của chúng, nó sẽ sinh ra các hồ IO mới và không chạm vào nhóm công nhân. Bạn có thể tùy chỉnh các số bằng cách sử dụng ThreadPool.SetMinThreads/SetMaxThreads, nhưng điều này thường là một dấu hiệu cho thấy quá trình của bạn lạm dụng threadpool.
  3. Các chủ đề IO được sử dụng khi có các mục được dequeued từ IOCP nội bộ của threadpool. Trong mã điển hình, điều này sẽ xảy ra khi một hoạt động không đồng bộ đã hoàn thành trên một số xử lý IO. Bạn cũng có thể tự xếp hàng các mục thông qua UnsafeQueueNativeOverlapped, nhưng ít phổ biến hơn nhiều.

Hoạt động không đồng bộ được quản lý thuần túy (ví dụ như thực hiện async-await với Task.Delay) không liên quan đến bất kỳ xử lý IO nào, và do đó chúng không được đăng lên IOCP bởi một số trình điều khiển. những thứ đó sẽ thuộc danh mục "Người lao động".

Như một lưu ý phụ, bạn có thể cho biết chuỗi công việc từ chủ đề IO bằng cách gọi. Chủ đề người lao động sẽ bắt đầu cuộc gọi được quản lý của họ với "ThreadPoolWorkQueue.Dispatch", trong khi các chủ đề IO sẽ bắt đầu callstack được quản lý của họ với "_IOCompletionCallback.PerformIOCompletionCallback". Đây là tất cả chi tiết triển khai có thể thay đổi bất kỳ lúc nào, nhưng có thể hữu ích khi biết bạn đang xử lý những gì khi gỡ lỗi mã được quản lý của mình.

Các vấn đề liên quan