簡體   English   中英

使用 `Task.Run` 或只調用異步方法而不使用 `await`

[英]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 更好?

一般來說,最好不要使用即發即忘。

“火而忘記”的意思是:

  1. 你不在乎代碼是否有異常。 任何異常都會導致它靜默失敗; 沒有日志記錄,沒有通知等。
  2. 您不需要知道代碼何時完成。 即,消費應用程序永遠不需要等待代碼完成。 即使在關機期間。
  3. 您不需要代碼即可完成。 作為 (2) 的推論,即發即棄的代碼可能無法運行完成; 並且作為 (1) 的推論,您不會收到它未能完成的通知。

簡而言之,“即發即忘”只適用於極少數的任務。 例如,更新緩存。 我說大概85%以上的“發射后不管”代碼是錯誤-它用火和忘記的代碼,應該是火和忘記。

所以我想說最好的解決方案是不使用火而完全忘記。 最起碼,你應該暴露一個Task是代表“后續行動”的地方。 考慮將Task添加到您的返回類型或將其作為屬性公開。

采用即發即棄——尤其是在圖書館中——意味着你迫使所有消費者永遠不知道何時關閉和退出是安全的。 但是,如果您真的想開火而忘,則有幾種選擇。

A. 一種選擇是在沒有上下文的情況下調用async void函數。 消費應用程序仍然無法確定代碼是否/何時完成,但至少不會忽略異常。

B. 另一種選擇是在沒有上下文的情況下開始任務。 此選項具有觸發和忘記代碼的缺點:忽略異常並且調用代碼無法知道它何時完成。

這兩個建議都是在沒有上下文的情況下開始任務的。 幫助程序可以執行此操作,或者您可以將調用包裝在Task.Run (效率稍低,但效果很好)。

我不建議直接啟動任務。 雖然這在控制台應用程序中可以正常工作,但它不適用於在提供上下文的情況下可能調用的庫。

一般來說,這取決於但Task.Run是更安全的選擇,其背后的推理取決於兩件事:

  1. 如果我們可以保證async方法始終是異步的。 對於庫項目,或者如果異步方法位於不受調用者管理的代碼中,保證方法始終是異步的可能會變得特別棘手。 例如,一旦我在庫中實現了類似於此示例的方法:
 public async Task HandleMessages(Func<Task> onMessageNotification)
 {
   while(true)
   {
      var msg = MessagingClient.ReceiveMessage();
      ...
     if(msg.MessageType == "MakeAPizza")
     { 
       _ = onMessageNotification();
       _ = MakePizzaAsync();
     }
   }
 }

 private async Task MakePizzaAsync() {...}

我想觸發並忘記onMessageNotificationMakePizzaAsync因為我不希望它阻止消息處理。 我沒有注意到的是當調用者實現如下onMessageNotification

Func<Task> onMsgNotification = () => {
  DoSlowOperation();
  return Task.CompletedTask;
}

這實際上會使onMessageNotification同步操作,並且必須等待DoSlowOperation()完成。 因此,為了避免解決問題,我只需要將其更改為:

  _ = Task.Run(onMessageNotification);
  _ = MakePizzaAsync();
  1. 在第一個await操作之前,我們不會在 async 方法中進行長時間運行的操作。 調用者可能導致問題的另一種方式是onMsgnotification Func 是否按以下方式實現:
Func<Task> onMsgNotification = async () => {
  DoSlowOperation();
  await AsyncOperation();
}

這仍然會減慢HandleMessages方法的速度,因為DoSlowOperation仍在同步調用。

暫無
暫無

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

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