長時間阻塞線程池的線程並不是一個好主意(因爲它們是有限的)。
線程池將通過注入一個新線程來響應線程的「丟失」,所以長時間阻塞線程池線程並不是特別有問題。 (當然,最好是而不是來首先阻塞線程池線程)。
任務使用線程池的線程,因此是短期的,不是長時間的阻塞操作。
這隻適用於調度到線程池的委託任務。圍繞任務的很多舊的(.NET 4時代)文檔都假設您正在使用線程池上安排的委託任務,所以這是對一般任務的常見誤解。
在現代世界中,委託任務更爲罕見;承諾現在任務更爲普遍。承諾任務只是充當「信號」,意思是「完成了某件事」。有關委託任務和承諾任務的更多信息,請參閱Task Overview和Task status上的博文。 (另外,代理任務可以將安排爲任何種類的TaskScheduler
;它們不僅僅用於線程池任務)。
異步方法(如實體框架中的FindAsync)會返回一個任務,您可以等待(或等待)以接收結果。
這裏有一個區別,需要使用async
其實施該結束*Async
的,旨在爲使用await
方法,並TAP methods之間進行。這兩個人通常在一起,但並非總是如此。
async
總是返回一個承諾任務,從來沒有一個委託任務。此外,一般預計一個*Async
方法將返回一個承諾任務,但一些方法,而不是返回委託任務。當方法做到這一點時,我稱之爲「假異步」,因爲它只是同步阻塞另一個線程。
如果我打電話給FindAsync是否只創建了一個在ThreadPool線程上運行並調用非async查找方法的任務(阻塞ThreadPool線程直到找到返回值)?
不是在一般情況下。幾乎所有由.NET提供的*Async
方法都返回Promise Tasks。
或者是否存在更深入的操作系統機制,並且在Find方法返回之前不使用ThreadPool線程?
大多數.NET *Async
方法返回無極任務使用I/O完成端口的引擎蓋下,我在我的文章There Is No Thread形容。
如果變體1成立,調用FindAsync方法或自己啓動一個任務並在其中調用Find方法沒有區別。
如果變體2成立,則存在差異,因爲啓動調用Find方法的任務將長期阻塞ThreadPool線程,而調用FindAsync則不會。
在一般情況下,您希望使用*Async
方法,這樣可以避免阻塞任何線程。
但是,正如其他人在評論中指出的那樣,Entity Framework的這個特殊示例有點複雜。實體框架本身是異步不可知的;它建立在「提供者」的基礎之上,可能會或可能不會支持異步。微軟的SqlClient提供程序不支持異步,因此與SQL Server交談的FindAsync
將正常工作異步。但是,其他提供者可能不會(在撰寫本文時,SQLite是不支持異步的通用提供者),對於這些提供者,實際上通過阻塞線程池線程來實現像FindAsync
這樣的「異步」API。
所以,在一般情況下,「變體2」是正確的;但對於你的特定例子FindAsync
,它們都是真實的。
Stephen Cleary寫了一篇很棒的文章[沒有線索](http://blog.stephencleary.com/2013/11/there-is-no-thread.html),你可能想閱讀。並且要記住'TaskCompletionSource'的存在 - 專門用於允許創建'任務',它肯定在*線程池上運行。 –
在這種特殊情況下,下面的SqlClient提供真正的異步執行而不使用後臺線程。 IO(包括網絡調用)在Windows中本質上是異步的。線程阻塞*模擬*。 ADO.NET使用IO完成端口與數據庫服務器通信並在收到響應後恢復處理。其他提供者(例如MySQL的)通過啓動新線程來僞造異步 –