[英]Fire and forget, using `Task.Run` or just calling an async method without `await`
注意:這是一個針對Asp.Net
和 Web 應用程序領域之外的問題。
一般來說,尤其是在涉及庫或控制台應用程序時,為了觸發並忘記異步方法,只調用異步方法而不使用await
或使用Task.Run
嗎?
基本上:
public static void Main(){
// Doing
_ = DoSomethingAsync();
// vs.
_ = Task.Run(DoSomethingAsync);
}
private static async Task DoSomethingAsync()
{
...
}
一般來說,特別是在涉及庫或控制台應用程序時,為了觸發並忘記異步方法,是只調用異步方法而不等待它還是使用 Task.Run 更好?
一般來說,最好不要使用即發即忘。
“火而忘記”的意思是:
簡而言之,“即發即忘”只適用於極少數的任務。 例如,更新緩存。 我說大概85%以上的“發射后不管”的代碼是錯誤-它用火和忘記的代碼,不應該是火和忘記。
所以我想說最好的解決方案是不使用火而完全忘記。 最起碼,你應該暴露一個Task
是代表“后續行動”的地方。 考慮將Task
添加到您的返回類型或將其作為屬性公開。
采用即發即棄——尤其是在圖書館中——意味着你迫使所有消費者永遠不知道何時關閉和退出是安全的。 但是,如果您真的想開火而忘,則有幾種選擇。
A. 一種選擇是在沒有上下文的情況下調用async void
函數。 消費應用程序仍然無法確定代碼是否/何時完成,但至少不會忽略異常。
B. 另一種選擇是在沒有上下文的情況下開始任務。 此選項具有觸發和忘記代碼的缺點:忽略異常並且調用代碼無法知道它何時完成。
這兩個建議都是在沒有上下文的情況下開始任務的。 有幫助程序可以執行此操作,或者您可以將調用包裝在Task.Run
(效率稍低,但效果很好)。
我不建議直接啟動任務。 雖然這在控制台應用程序中可以正常工作,但它不適用於在提供上下文的情況下可能調用的庫。
一般來說,這取決於但Task.Run
是更安全的選擇,其背后的推理取決於兩件事:
async
方法始終是異步的。 對於庫項目,或者如果異步方法位於不受調用者管理的代碼中,保證方法始終是異步的可能會變得特別棘手。 例如,一旦我在庫中實現了類似於此示例的方法: public async Task HandleMessages(Func<Task> onMessageNotification)
{
while(true)
{
var msg = MessagingClient.ReceiveMessage();
...
if(msg.MessageType == "MakeAPizza")
{
_ = onMessageNotification();
_ = MakePizzaAsync();
}
}
}
private async Task MakePizzaAsync() {...}
我想觸發並忘記onMessageNotification
和MakePizzaAsync
因為我不希望它阻止消息處理。 我沒有注意到的是當調用者實現如下onMessageNotification
:
Func<Task> onMsgNotification = () => {
DoSlowOperation();
return Task.CompletedTask;
}
這實際上會使onMessageNotification
同步操作,並且必須等待DoSlowOperation()
完成。 因此,為了避免解決問題,我只需要將其更改為:
_ = Task.Run(onMessageNotification);
_ = MakePizzaAsync();
await
操作之前,我們不會在 async 方法中進行長時間運行的操作。 調用者可能導致問題的另一種方式是onMsgnotification
Func 是否按以下方式實現:Func<Task> onMsgNotification = async () => {
DoSlowOperation();
await AsyncOperation();
}
這仍然會減慢HandleMessages
方法的速度,因為DoSlowOperation
仍在同步調用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.