[英]Does await Task.CompletedTask mean the async method will run synchronously?
[英]Why does the C# compiler handle empty async Task methods different to just return Task.CompletedTask?
你好,
編輯:該帖子被編輯為不使用空方法以更清楚地了解我想知道的內容。
C# 編譯不使用await
返回Task.CompletedTask
的情況下不編譯async Task
方法是否有原因?
有時有必要實現一個返回Task
但沒有使用await
的方法。
我想知道生成的 IL 代碼在這兩種方法之間是否存在如此不同的原因:
public async Task DoNothing()
{
Console.WriteLine("nothing");
}
public Task DoNothing()
{
Console.WriteLine("nothing");
return Task.CompletedTask;
}
我創建了兩個 Fiddles,可以在其中查看生成的 IL 代碼。
async Task
: https://dotnetfiddle.net/UQuJUh
Task.CompletedTask
: https://dotnetfiddle.net/eRr4i1
那么編譯器不優化代碼以以相同方式處理這兩種情況的原因是什么? 謝謝你。
在這兩段代碼中處理異常的方式存在顯着差異,因此它們是不等價的:
public async Task DoNothing()
{
Console.WriteLine("nothing");
}
在Console.WriteLine
引發異常的情況下,異常實際上是在內部捕獲並存儲在結果Task
中,由Task.IsFaulted
指示。 只有當您在任務上使用await
時,才會真正“拋出”異常。
public Task DoNothing()
{
Console.WriteLine("nothing");
return Task.CompletedTask;
}
然而,這種情況是完全同步的; 來自Console.WriteLine
的潛在異常甚至在任何任務可以被返回或處理之前就會被引發。 我認為這實際上比第一個選項更不安全,因為這種行為可能是出乎意料的。 迭代器對yield break;
並return Array.Empty<object>();
.
現在您的問題最初詢問了一個實際上是空的方法。 好吧,可能是該方法實際上不是空的; 將來它可能包含調試模式下的斷點、注入代碼或可能為 arguments 自動生成的檢查。 即使是空的方法體,仍然會生成空的try
塊:
.try
{
IL_0000: leave.s IL_0019
} // end .try
catch [mscorlib]System.Exception
{
現在我可以想象,對於真正空的方法,可以將生成async
方法的異常添加到編譯器中,但這只會使代碼復雜化,而沒有(可以說)真正的收益。 可能會有一個空的async
方法(例如給其他可能想要在未來實現它的程序員一個提示),但是你不需要那么關心性能來保證這樣的優化(無論如何你都有一個簡單的解決方案)。
(有趣的旁注:C# 11 可能會從標有!!
的參數中引入ArgumentNullException
的自動引發。此時,異常實際上是在async
/iterator 方法的代碼之外,在調用之后立即引發的,因此可以說方法體,或者它的結果,在技術上仍然是空的。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.