2013-05-21 40 views
5

According to MSDN, asyncawait không tạo chủ đề mới:Hiểu async/chờ đợi mà không đề

Các asyncawait từ khóa không gây đề bổ sung để được tạo ra.

Với ý nghĩ này, tôi gặp khó khăn trong việc hiểu luồng điều khiển của một số chương trình đơn giản. Ví dụ hoàn chỉnh của tôi là dưới đây. Lưu ý rằng nó yêu cầu Dataflow library, mà bạn có thể cài đặt từ NuGet.

using System; 
using System.Threading.Tasks.Dataflow; 

namespace TaskSandbox 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      BufferBlock<int> bufferBlock = new BufferBlock<int>(); 

      Consume(bufferBlock); 
      Produce(bufferBlock); 

      Console.ReadLine(); 
     } 

     static bool touched; 
     static void Produce(ITargetBlock<int> target) 
     { 
      for (int i = 0; i < 5; i++) 
      { 
       Console.Error.WriteLine("Producing " + i); 
       target.Post(i); 
       Console.Error.WriteLine("Performing intensive computation"); 
       touched = false; 
       for (int j = 0; j < 100000000; j++) 
        ; 
       Console.Error.WriteLine("Finished intensive computation. Touched: " + touched); 
      } 

      target.Complete(); 
     } 

     static async void Consume(ISourceBlock<int> source) 
     { 
      while (await source.OutputAvailableAsync()) 
      { 
       touched = true; 
       int received = source.Receive(); 
       Console.Error.WriteLine("Received " + received); 
      } 
     } 
    } 
} 

Output:

Producing 0 
Performing intensive computation 
Received 0 
Finished intensive computation. Touched: True 
Producing 1 
Performing intensive computation 
Received 1 
Finished intensive computation. Touched: True 
Producing 2 
Performing intensive computation 
Received 2 
Finished intensive computation. Touched: False 
Producing 3 
Performing intensive computation 
Received 3 
Finished intensive computation. Touched: False 
Producing 4 
Performing intensive computation 
Received 4 
Finished intensive computation. Touched: True 

Điều này dường như chỉ ra rằng Consume được đưa ra kiểm soát trong khi for vòng lặp đang chạy, như là nhiệm vụ OutputAvailableAsync hoàn thành:

for (int j = 0; j < 100000000; j++) 
    ; 

Đây sẽ là ngạc nhiên trong một mô hình luồng. Nhưng nếu không có chủ đề bổ sung nào có liên quan, làm cách nào để có thể kiểm soát lợi nhuận Produce ở giữa vòng lặp for?

+0

@ I4V: Câu trả lời cho câu hỏi đó cho biết rằng "tất cả các hoạt động chặn phải kiểm soát rõ ràng việc kiểm soát bằng mô hình async/await". Nhưng trong câu hỏi của tôi, kiểm soát chuyển từ 'Produce' sang' Consume' mà không có 'Produce' kiểm soát rõ ràng. Đó là phần mà tôi đang bối rối. –

+3

@Matthew Vì đây là một ứng dụng Console không có 'SynchronizationContext', có nghĩa là tất cả các cuộc gọi lại từ các cuộc gọi' await' đi đến 'SynchronizationContext.Default', là nhóm luồng, vì vậy về mặt kỹ thuật, có hai luồng đang chạy đôi khi trong quá trình thực hiện chương trình này. Để ngăn chặn việc tạo ra các chủ đề bổ sung, bạn cần phải tạo bối cảnh đồng bộ tùy chỉnh của riêng bạn và thiết lập đó. Nếu bạn đã làm, bạn sẽ thấy rằng các cuộc gọi 'Recieved' sẽ không được gọi cho đến khi tất cả các sản xuất được thực hiện cụ thể bởi vì bạn * không * kiểm soát năng suất trong khi sản xuất. – Servy

+0

@Servy: Nếu có nhiều chủ đề liên quan, tại sao MSDN cho rằng "Đặc biệt, cách tiếp cận này tốt hơn BackgroundWorker cho các hoạt động IO-bound vì ... bạn không phải bảo vệ chống lại điều kiện chủng tộc"? Tôi đã chỉnh sửa ví dụ của mình để thêm một điều kiện đua đơn giản. –

Trả lời

-1

Việc xử lý điều khiển được thực hiện trong cùng một luồng, để khi vòng lặp đang chạy, phương pháp Consume không phải là và ngược lại. Nếu bạn đang sử dụng các chủ đề, nó có thể không nhất thiết phải như vậy, và trên thực tế bạn mong muốn cả hai chạy đồng thời.

Thực tế là chúng nằm trong cùng một chuỗi không có nghĩa là bất kỳ cách nào mà kiểm soát không thể chuyển từ một phần của mã này sang mã khác. .NET (và mọi khung công tác khác, AFAIK) xử lý trơn tru, và mỗi phần chạy với bối cảnh riêng của nó mà không có vấn đề gì.

Tuy nhiên, chạy cả hai thứ trong một chuỗi có nghĩa là trong khi Consume đang chạy, vòng lặp sẽ "treo". Nếu Consume mất quá nhiều thời gian, đó chính là điều người dùng có thể cảm nhận. Đó là lý do tại sao nhiều lập trình viên mới làm quen với các biểu mẫu windows ngạc nhiên khi lấp đầy các điều khiển GUI với quá nhiều thông tin cùng một lúc khiến các biểu mẫu của họ bị treo và đôi khi bị trống - luồng làm mới màn hình giống với luồng logic điều khiển, nếu bạn không sử dụng chuỗi công nhân nền.

+0

Điều gì khiến kiểm soát chuyển sang 'Tiêu thụ' mà không có' Sản xuất 'kiểm soát rõ ràng năng suất? Có. NET thông báo rằng 'Produce' đã chạy trong một thời gian dài, và cung cấp cho điều khiển để' Consume' (tương tự như vai trò của hệ điều hành trong một mô hình luồng)? –

+0

Thực ra tôi nghĩ Servy có câu trả lời tốt hơn tôi trong phần bình luận. – Renan

2

nếu không có đề tài bổ sung nào có liên quan, làm cách nào để kiểm soát sản lượng ở giữa vòng lặp?

Ai nói không có chủ đề bổ sung nào có liên quan? Thực tế là bạn đã tuyên bố là:

Từ khóa không đồng bộ và đang chờ không tạo ra chủ đề bổ sung được tạo.

Điều này hoàn toàn đúng. chương trình của bạn bao gồm các mảnh vỡ

target.Post(i); 

await source.OutputAvailableAsync()) 

tôi đoán sẽ là cuộc gọi đến target.Post(i) hoặc source.OutputAvailableAsync() tạo ra một chủ đề.await không tạo ra một chuỗi; tất cả các await hiện là gán phần còn lại của phương thức như việc tiếp tục nhiệm vụ được trả về bởi cuộc gọi và sau đó trả về điều khiển cho người gọi. Nếu nhiệm vụ đó sinh ra một sợi chỉ để thực hiện công việc của mình, đó là kinh doanh.

await chỉ là một luồng điều khiển khác; một dòng điều khiển rất phức tạp, để chắc chắn, nhưng một luồng điều khiển. Nó không phải là một cú pháp để tạo ra các luồng; đó là một đường cú pháp để gán một sự tiếp tục cho một nhiệm vụ.

+4

Thực tế, cả hai cuộc gọi đó đều không tạo chủ đề. @Servy là chính xác trong đó toán tử 'await' (cụ thể hơn, công việc chờ đợi được sử dụng bởi mã được tạo bởi toán tử' await') đang sử dụng 'SynchronizationContext' mặc định để lên lịch các phép tiếp tục phương thức trên các luồng thread thread. –

+0

@StephenCleary: Ah, tốt nhất. Cảm ơn ghi chú. –

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