2016-04-13 42 views
1

這2個代碼片段有什麼區別? 如果我們有以下匿名函數:直接調用匿名異步函數與使用Task.Factory之間的區別?

Func<object, Task<bool> foo = async (str) => { /*misc async code with awaits*/ return result};

什麼是直接調用它的區別是:

Task<bool> bar = foo("myparam");

,並使用一個任務:

Task<bool> bar = Task.Factory.StartNew(foo, "myparam").Unwrap();

在本質,他們都執行異步唉聲嘆氣,但親們的做法是什麼?他們都返回任務。返回的任務如何不同? (例如是一個多線程的,而其他單線程等)

+0

'foo(「myparam」)'在當前線程中調用'foo'。用當前的任務調度程序(或任何爲工廠配置的任務調度程序)調用'StartNew'調度'foo'。 – PetSerAl

+0

基本上同樣的問題以不同的方式提出和回答:http://stackoverflow.com/questions/34680985/what-is-the-difference-between-asynchronous-programming-and-multithreading – Kilanash

+0

@Kilanash,閱讀後。你的話說,直接調用,將​​啓動一個相同的線程異步任務,而工廠,可能'產生一個單獨的線程?從我對Task的理解(以及默認的Factory)的理解中,不能保證會創建一個新的線程。 – WillFM

回答

2

從本質上說,他們都執行異步,但什麼都做了另一種方式的親的/反對的?

直接調用該方法將在當前線程上異步執行它。被調用的方法將繼承調用方法的上下文,並將使用當前SynchronizationContext(或者,如果是null,當前TaskScheduler)恢復執行。 I explain this in full on my blog

通過StartNew調用方法將在當前的TaskScheduler上異步執行。通常,這是線程池任務調度程序,除非調用代碼正在作爲Delegate Task(我的博客中定義的術語)的一部分執行。除非該任務是started with the HideScheduler option(在我的博客中描述),在這種情況下,即使有TaskScheduler執行該代碼,當前也不存在TaskScheduler

如果StartNew情況聽起來很複雜,那是因爲它。 StartNew只適用於專家。我有一個關於why StartNew should not be used的完整博客文章。

更直觀的比較是直接調用方法和通過Task.Run調用方法。與StartNew不同,Task.Run始終在線程池線程上執行其代碼,因此該方法將在線程池上異步運行。

對於真實世界的代碼,您只能在需要時使用Task.Run。如果該方法是正確異步的(即,它不首先計算分形或任何東西),則不應使用Task.Run。而且你根本不應該使用StartNew

+0

謝謝斯蒂芬,這幾乎是我正在尋找的澄清。我在異步匿名動作/ funcs上找到的大多數示例。讓人們使用Task.Factory.StartNew或Task.Run。在編寫異步代碼時,它對我來說是多餘的,這就是我尋求澄清的原因。 – WillFM

0

第一呼叫「任務欄= FOO(‘myparam’)`返回表示的函數功能

實際執行任務第二個電話Task.Factory.StartNew返回Task<Task<bool>>表示返回另一個Task<bool>此時代表拉姆達

UnWrap()調用返回一個代表內任務的完成一個新的代理任務的執行異步操作。

1

我在https://dotnetfiddle.net/v1kFjw上面做了一個快速的例子,它創建了func,爲它分配了一些潛在的長時間運行的工作,在它周圍添加了一些調試打印語句作爲時間線引用,然後以兩種方式調用它。

使用視圖IL功能,下面似乎是差,作爲korir指出:

直接調用FUNC周圍產生代碼直接FUNC內部的async/await狀態機模式,則開始運行代碼直到它到達await,在這個狀態機器上,'返回'到原來的調用者(當我說返回時,它不是一個字面的返回值,它是調用後面的代碼被無聲地包裝到狀態中機器),並繼續與調用等待的代碼進行交錯,輪詢完成。

使用Task.Factory.StartNew創建一個新的線程,無論代碼如何跟隨它都會運行 - 這包括程序可能在您啓動的線程完成之前退出。

在該示例中,必須添加一個顯示完成的延續任務,並在末尾添加一個Task.WaitAll以確保完成。

我只是在爲感興趣的緣故做研究 - 如果任何人有更多的見解或可以指出缺陷請做。

編輯:在發佈這些內容後,我需要注意幾件事:dotnetfiddle有影響測試結果的執行時間和輸出大小限制 - 最後我使用Linqpad進行測試,因爲這樣做更可靠。還注意到,調用異步函數但不使用返回值似乎再次遇到了由於主線程在結果結束之前退出而不能保證函數的完整運行時間的問題。 在回答你的意見關於.Unwrap()沒有被使用 - ContinueWith()似乎不符合它,至少根據編譯器。

最後,我順從斯蒂芬克萊裏的答案,因爲他顯然更有知識和合格。 :)

參考文獻:

+0

請告訴我的區別: 和 '任務巴= Task.Factory.StartNew(() '任務巴= Task.Factory.StartNew(FOO, 「myparam」)//工廠通過state' => FOO( 「myparam」))//隱式傳遞狀態' – WillFM

+0

另外,你還沒有解開工廠任務,就像我在示例中顯示的那樣。有趣的是,'ContinueWith'在從直接調用返回的任務中產生時在新線程中運行。而使用工廠似乎使用相同的線程。 – WillFM

+0

通過StartNew傳遞狀態和使用lambda隱式傳遞狀態之間的區別僅僅是靜態類型意識。在第一種情況下,「myparam」是使用object傳遞的,你必須在你的任務函數中強制轉換狀態(在運行時可能導致類型轉換錯誤),而使用lambda可以獲得編譯時類型檢查。 – Kilanash

相關問題