2014-02-19 285 views
21

我很好奇異步等待線程內部結構。異步等待線程內部結構

每個人都表示異步在性能方面比較好,因爲它釋放了正在等待長時間異步調用響應的線程。 好吧,我明白了。

但讓我們來考慮這種情況。

我有一個異步方法在數據庫上執行異步操作。 數據庫的api暴露函數BeginQuery和事件QueryCompleted。 我用一個任務(使用TaskCompletionSource)包裝了它們。

我的問題是調用BeginQuery和觸發事件QueryCompleted之間的內幕。

我的意思是 - 是否需要培育某種工作人員來開展活動?在非常低的級別,它必須是一些同步循環,阻止來自db的線程讀取結果。

我想,任何異步調用都必須生成一個線程來實際處理響應(也許等待它在驅動程序代碼中的低級C++循環中)。

所以我們唯一的「收穫」就是當其他線程正在工作時,調用者線程可以被釋放。

調用異步方法是否總是創建一個新的工作線程?

有人可以證實我的理解?

+4

我不認爲「每個人都表示異步在性能方面更好」。異步/等待可以使應用程序更具響應性,因爲它可以更輕鬆地使用用戶界面的任務和延續。 – Dirk

+0

異步/等待是比線程更高層次的抽象。這些操作在SynchronizationContext上同步,並且引擎的實現可能會或可能不會創建額外的線程。見例如[這是所有關於SynchronizationContext](http://msdn.microsoft.com/en-us/magazine/gg598924.aspx)。 – GSerg

回答

33

每個人都指出,異步是如此美好的性能的情況下,因爲它釋放正在等待以久的異步調用的響應線程。

是和否。 async背後的要點是釋放調用線程。在UI應用程序中,async的主要好處是響應性,因爲UI線程被釋放。在服務器應用程序中,async的主要好處是可伸縮性,因爲請求線程被釋放以處理其他請求。

所以我們唯一的「收穫」就是調用者線程可以在其他線程正在工作時被釋放。 總是調用一個異步方法創建一個新的工作線程?

否。在OS級別,所有I/O都是異步的。正在進行底層異步I/O時,它是阻塞線程的同步API。我最近在博客文章中寫到:there is no thread

+0

謝謝你的回答。與鏈接的博客一起,這是非常全面的講座。但我有個問題。如果我想編寫一個利用異步I/O的庫,我該從哪裏開始? 我是否需要編寫一些非託管代碼?這可以純粹用C#完成嗎? – mbudnik

+0

它可以在純粹的託管代碼中完成,但是這樣做的指示很難找到。 [這是最好的概述](http://www.beefycode.com/post/Using-Overlapped-IO-from-Managed-Code.aspx)。 –

+0

Bumping [沒有線程](http://blog.stephencleary.com/2013/11/there-is-no-thread.html) – bvj

1

異步方法不創建新的線程,它們是基於Task s,這可能會或可能不會使用線程(如TaskCompletionSource),因爲他們使用ThreadPool,即使他們這樣做有沒有開銷。

3

我的意思是 - 是否需要產生某種工作人員來觸發 事件?在非常低的級別,它必須是一些同步循環,即 阻止來自db的線程讀取結果。

即使你確實有等待內核對象(如手動重置事件),你仍然可以從堵轉阻塞同步代碼到一個異步和釋放線程(更新:a real-life scenario)。

例如,同步代碼:

void Consume() 
{ 
    var completedMre = new ManualResetEvent(false); 

    producer.StartOperation(completedMre); 

    completedMre.WaitOne(); // blocking wait 

    Console.WriteLine(producer.DequeueResult()); 
} 

異步模擬:

async Task ConsumeAsync() 
{ 
    var completedMre = new ManualResetEvent(false); 

    producer.StartOperation(completedMre); 

    var tcs = new TaskCompletionSource<Result>(); 

    ThreadPool.RegisterWaitForSingleObject(completedMre, 
     (s, t) => tcs.SetResult(producer.DequeueResult()), 
     null, Timeout.Infinite, true); 

    var result = await tcs.Task; 
    Console.WriteLine(result); 
} 

異步版本秤更好最多64倍(MAXIMUM_WAIT_OBJECTS,這是其可以是內核對象的最大數目通過RegisterWaitForSingleObject彙總,用於在單個線程上等待)。所以,你可以並行調用Consume() 64次,它會阻塞64個線程。或者你可以撥打ConsumeAsync 64次,它只會阻塞一個線程。

5

我的意思是 - 是否需要產生某種工作人員來觸發 事件?在非常低的級別,它必須是一些同步循環,即 阻止來自db的線程讀取結果。

它將創建一個IO完成端口(IOCP),表示正在外部處理的任務,該線程將繼續執行其他任務。然後,當IOCP通知任務完成時,一個線程將接收IOCP的狀態並繼續執行任務。

http://www.drdobbs.com/cpp/multithreaded-asynchronous-io-io-comple/201202921

I/O完成端口提供了一個優雅的解決方案的 編寫使用多線程和 異步I/O可擴展的服務器應用程序的問題。

+1

This!在操作系統上使用IOCP比完整的線程更容易。您的操作系統只能處理幾千個線程,但可以輕鬆處理幾百萬個完成端口。因此,通過異步/等待,您可以限制大量繁重線程的數量,並使用更輕鬆處理的稱爲I/O完成端口的輕量級操作系統對象。 –