2015-01-18 9 views
9

Tôi đã đọc (và sử dụng) không đồng bộ/đang chờ đợi khá nhiều trong một thời gian nhưng tôi vẫn có một câu hỏi mà tôi không thể nhận được câu trả lời. Giả sử tôi có mã này.Làm cách nào để chạy một số tác vụ không đồng bộ trên chuỗi giao diện người dùng bằng cách sử dụng công việc không đồng bộ/đang chờ?

private async void workAsyncBtn_Click(object sender, EventArgs e) 
{ 
    var myTask = _asyncAwaitExcamples.DoHeavyWorkAsync(5); 
    await myTask; 
    statusTextBox.Text += "\r\n DoHeavyWorkAsync message"; 
} 

Nó được gọi là từ thread UI và trở về giao diện người dùng đề. Do đó tôi có thể thực hiện các giao diện người dùng cụ thể trong phương thức này và sau await myTask. Nếu tôi đã sử dụng .ConfigureAwait(false) Tôi sẽ nhận được một ngoại lệ thread khi thực hiện statusTextBox.Text += "\r\n DoHeavyWorkAsync message"; vì tôi đã nói với myTask bạn có thể lấy bất kỳ chuỗi nào có sẵn từ nhóm chủ đề.

Câu hỏi của tôi. Theo tôi hiểu, tôi không bao giờ rời khỏi giao diện người dùng trong trường hợp này, nó vẫn chạy không đồng bộ, giao diện người dùng vẫn đáp ứng và tôi có thể bắt đầu một số Tác vụ cùng một lúc và tăng tốc cho ứng dụng của tôi. Làm thế nào có thể làm việc này nếu chúng ta chỉ sử dụng một sợi?

Cảm ơn!

EDIT cho Sievajet

private async void workAsyncBtn_Click(object sender, EventArgs e) 
{ 
    await DoAsync(); 
} 

private async Task DoAsync() 
{ 
    await Task.Delay(200); 
    statusTextBox.Text += "Call to form"; 
    await Task.Delay(200); 
} 
+0

Bạn đặc biệt nói về marshaling của công việc trở lại thread UI? –

+0

Tôi bây giờ thấy rằng câu hỏi không phải là cụ thể như tôi nghĩ, rằng nó có thể được diễn giải theo những cách khác nhau. Tôi đã hiểu rõ hơn về cách đọc câu trả lời của bạn @YuvalItzchakov và sau khi đọc câu trả lời của Sievajet tôi thấy có một câu trả lời khác khi không có hoạt động liên kết IO không đồng bộ. – Andreas

+1

@Andreas Tôi thấy bạn đã thêm một ví dụ khác và nó hoạt động vì một lần nữa: không có chủ đề nào liên quan. Bạn đã đọc vào http://blog.stephencleary.com/2013/11/there-is-no-thread.html, theo đề xuất của Yuval chưa? – Krumelur

Trả lời

10

Theo tôi được biết tôi không bao giờ rời khỏi thread UI trong trường hợp này, vẫn nó chạy không đồng bộ, giao diện người dùng vẫn đáp ứng và tôi có thể bắt đầu vài Nhiệm vụ tại đồng thời tăng tốc cho ứng dụng của tôi. Làm cách nào để làm việc này nếu chúng tôi chỉ sử dụng một chuỗi?

Trước tiên, tôi khuyên bạn nên đọc bài đăng trên blog Stephan Clearys - There is no thread.

Để hiểu làm thế nào có thể của nó để chạy nhiều đơn vị công việc hoàn toàn, chúng ta cần phải nắm bắt một thực tế quan trọng: async IO ràng buộc hoạt động có (gần) không có gì để làm với chủ đề.

Làm cách nào có thể? tốt, nếu chúng ta đi sâu vào hệ điều hành, chúng ta sẽ thấy các cuộc gọi đến trình điều khiển thiết bị - những người chịu trách nhiệm thực hiện các hoạt động như cuộc gọi mạng và ghi vào đĩa, đều được triển khai như tự nhiên không đồng bộ, chúng không chiếm một sợi chỉ trong khi thực hiện công việc của chúng. Bằng cách đó, trong khi trình điều khiển thiết bị đang làm việc của nó, không cần phải có một chủ đề. chỉ khi trình điều khiển thiết bị hoàn thành việc thực thi, nó sẽ báo hiệu hệ điều hành được thực hiện thông qua IOCP (cổng hoàn thành I/O), sau đó thực hiện phần còn lại của cuộc gọi phương thức (điều này được thực hiện trong .NET thông qua threadpool, trong đó có các chủ đề IOCP chuyên dụng).

Stephans blog của bài chứng minh này độc đáo:

Going down the async rabbit hole

Khi hệ điều hành thực hiện các DPC (hoãn lại gọi thủ tục) và xếp hàng IRP (I/O Request Packet), nó là một công việc về cơ bản được thực hiện cho đến khi trình điều khiển thiết bị báo hiệu nó trở lại với Tôi đã thực hiện thư, làm cho toàn bộ chuỗi hoạt động (được mô tả trong bài đăng trên blog) thực thi, cuối cùng sẽ kết thúc bằng cách gọi mã của bạn.

Một điều cần lưu ý là .NET thực hiện một số "phép thuật" cho chúng ta sau hậu trường khi sử dụng mẫu async-await. Có một điều được gọi là "bối cảnh đồng bộ hóa" (bạn có thể tìm thấy một lời giải thích khá dài dòng here).Bối cảnh đồng bộ này là whats phụ trách của việc gọi tiếp tục (mã sau khi await) đầu tiên trên thread UI trở lại một lần nữa (ở những nơi mà bối cảnh như vậy tồn tại).

Edit:

Cần lưu ý rằng sự kỳ diệu với bối cảnh đồng bộ xảy ra cho CPU hoạt động bị ràng buộc cũng như (và thực sự đối với bất kỳ đối tượng awaitable), vì vậy khi bạn sử dụng một sợi threadpool qua Task.Run hoặc Task.Factory.StartNew , điều này cũng sẽ hoạt động.

+1

Tôi không nghĩ OP hỏi về I/O không chặn trong phương thức async, tôi nghĩ rằng anh ta hỏi về thực tế là không sử dụng 'ConfigureAwait (false)', anh ta có thể có nhiều tác vụ đồng thời truy cập vào giao diện người dùng hiện tại ngữ cảnh (tức là sử dụng chuỗi giao diện người dùng hiện tại). Câu trả lời có lẽ là dọc theo dòng mà nhiệm vụ đồng thời không nhất thiết phải thực hiện trên nhiều chủ đề khác nhau. –

+0

@Andreas Tôi có thể đã hiểu nhầm câu hỏi. Có lẽ OP có thể làm rõ? –

+0

Tôi nghĩ rằng tôi nhận được nó ngay bây giờ. Nếu chúng tôi đã thực hiện WriteToDisk theo cách đồng bộ, chúng tôi đã bắt đầu hoạt động và kiên nhẫn chờ nó quay trở lại. Khi thực hiện những điều này, chúng ta bắt đầu hoạt động (bất kể nó là gì) đi bộ và để cho hoạt động (thường không cần một chuỗi) cho chúng ta biết khi nào nó được thực hiện. Có chính xác (chưa được đơn giản hóa) đúng không? – Andreas

4

Các TaskParallelLibrary (TPL) sử dụng một TaskScheduler có thể được cấu hình với TaskScheduler.FromCurrentSynchronizationContext để trở về SynchronizationContext như thế này:

textBox1.Text = "Start"; 
// The SynchronizationContext is captured here 
Factory.StartNew(() => DoSomeAsyncWork()) 
.ContinueWith( 
    () => 
    { 
     // Back on the SynchronizationContext it came from    
     textBox1.Text = "End"; 
    },TaskScheduler.FromCurrentSynchronizationContext()); 

Khi một phương pháp async tạm ngưng tại một await, theo mặc định nó sẽ nắm bắt được hiện SynchronizationContext và marshall mã sau khi chờ đợi trở lại trên SynchronizationContext nó đến từ.

 textBox1.Text = "Start"; 

     // The SynchronizationContext is captured here 

     /* The implementation of DoSomeAsyncWork depends how it runs, this could run on the threadpool pool 
      or it could be an 'I/O operation' or an 'Network operation' 
      which doesnt use the threadpool */ 
     await DoSomeAsyncWork(); 

     // Back on the SynchronizationContext it came from 
     textBox1.Text = "End"; 

async và chờ dụ:

async Task MyMethodAsync() 
{ 
    textBox1.Text = "Start"; 

    // The SynchronizationContext is captured here 
    await Task.Run(() => { DoSomeAsyncWork(); }); // run on the threadPool 

    // Back on the SynchronizationContext it came from 
    textBox1.Text = "End"; 
} 
+0

Hmm. Mayby Tôi không hiểu nhưng trong DoSomeAsyncWork tôi vẫn có thể gọi một phần tử giao diện người dùng (nói trong một ứng dụng biểu mẫu) và thiết lập các giá trị cho nó. Làm thế nào điều đó có thể hoạt động nếu mã được marshalled trở lại bối cảnh đầu tiên đang chờ đợi? – Andreas

+0

Bạn không thể làm điều đó – Sievajet

+0

Hãy nhìn vào chỉnh sửa của tôi – Andreas

1

Khi cuộc gọi thread UI đang chờ đợi nó bắt đầu hoạt động async và trả về ngay lập tức. Khi hoạt động async hoàn thành, nó sẽ thông báo một luồng từ nhóm luồng nhưng việc triển khai nội bộ của async đang chờ gửi đi thực thi tới luồng UI sẽ tiếp tục thực thi mã sau khi chờ.

Công văn được thực hiện bằng phương tiện SynchronizationContext mà lần lượt gọi System.Windows.Forms.Control.BeginInvoke.

CLR thông qua C# (4th Edition) (nhà phát triển tham khảo) phiên bản thứ 4 của trang Jeffrey Richter 749

Trên thực tế, Jeffrey làm việc với MS để thực hiện các async/chờ đợi lấy cảm hứng từ AsyncEnumerator mình

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