簡體   English   中英

Task.Run 下是否需要 await / async 關鍵字?

[英]Are the await / async keywords needed under Task.Run?

我有一個包裹在 Task.Run 下的異步 lambda 表達式。 但是,看起來我可以刪除 async 和 await 關鍵字,它們會產生相同的結果。

t1 = Task.Run(() => DoSomethingExpensiveAsync());
t2 = Task.Run(() => DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);

對比

var t1 = Task.Run(async () => await DoSomethingExpensiveAsync());
var t2 = Task.Run(async () => await DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);
  1. 為什么編譯器讓我這樣做以及幕后發生了什么?
  2. 是否存在添加它們會產生影響的情況?

實際上有三種變體。

var task = Task.Run(() => DoSomethingExpensiveAsync());

^ 這個函數聲明了一個新的匿名非異步函數,它調用DoSomethingExpensiveAsync()並返回它的Task 編譯器編譯這個匿名函數並將其作為參數傳遞給Task.Run()

var task = Task.Run( async () => await DoSomethingExpensiveAsync() );

^ 這個函數聲明了一個新的匿名異步函數,它調用DoSomethingExpensiveAsync() 然后它返回一個未完成的Task ,等待DoSomethingExpensiveAsync()完成,然后發出任務完成的信號。

var task = Task.Run(DoSomethingExpensiveAsync);

^ 這個根本沒有聲明一個新的匿名函數。 DoSomethingExpensiveAsync的直接引用將作為參數傳遞給Task.Run()

所有這些都是有效的,因為所有三個版本都返回一個Task並因此匹配接受Func<Task>Task.Run()的重載。

作為一個黑匣子,所有三個調用最終都會做同樣的事情。 然而,前兩個導致編譯一個新函數(盡管我不確定它不會被優化掉),而第二個也會導致為它創建另一個狀態機。

如果我們在不使用 lambda 表達式或匿名函數的情況下重寫它們,差異可能會更清楚。 以下代碼完全相同:

//This is the same as Task.Run( () => DoSomethingExpensiveAsync());
Task Foo()
{
    return DoSomethingExpensiveAsync();
}
var task = Task.Run(Foo);

//This is the same as Task.Run(async () => await DoSomethingExpensiveAsync());
async Task Bar()
{
    return await DoSomethingExpensiveAsync();
}
var task = Task.Run(Bar);

這兩者之間的區別在於,一個“省略”了任務,而另一個則沒有。 Stephen Cleary 寫了一篇關於這個主題完整博客

為什么編譯器讓我這樣做以及幕后發生了什么?

您正在調用的Task.Run的重載需要一個Func<Task> - 即一個Task返回函數。 Task來自哪里並不重要; 該函數只需要從某個地方返回它。

如果您在沒有asyncawait情況下傳遞委托,則該委托只是調用Task返回函數並返回相同的Task 如果您使用asyncawait傳遞委托,則委托會調用Task返回函數並await它; 從委托返回的實際Taskasync關鍵字創建的

在這種情況下,兩者在語義上是等價的。 使用async / await關鍵字效率稍低,因為編譯器為async委托創建了一個狀態機。

是否存在添加它們會產生影響的情況?

是的 在一般情況下,您應該保持asyncawait 只有在非常簡單的“直通”情況下才能刪除它們,比如這里的情況。

你的代碼是一樣的

t1 = DoSomethingExpensiveAsync();
t2 = DoSomethingExpensiveAsync();
await Task.WhenAll( t1, t2 );

因為Task.Run( Func< function )將返回由函數生成的任務的代理 沒有創建其他任務,因此您正在等待原始任務。

如果您已經有了異步方法,則根本不需要使用Task.Run

如果不知道DoSomethingExpensiveAsync是什么,就無法確定會發生什么。

讓我們假設DoSomethingExpensiveAsync是這樣的:

async Task DoSomethingExpensiveAsync()
{
     SynchronousMethod();
     await AsynchronousMethod();
}

在第一個片段中, Task.Run會將DoSomethingExpensiveAsync的調用調度到線程池,並在SynchronousMethod返回后立即返回。

在第二個代碼段中, Task.Run會將DoSomethingExpensiveAsync的調用調度到線程池,並在DoSomethingExpensiveAsyncTask返回完成時返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM