本文翻譯自:How to safely call an async method in C# without await
I have an async
method which returns no data: 我有一個不返回任何數據的async
方法:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
I'm calling this from another method which returns some data: 我從另一個返回一些數據的方法中呼叫此方法:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Calling MyAsyncMethod()
without awaiting it causes a " Because this call is not awaited, the current method continues to run before the call is completed " warning in visual studio. 在MyAsyncMethod()
呼叫MyAsyncMethod()
而不等待它會導致「 因爲未等待此呼叫,因此當前方法將在呼叫完成之前繼續執行 」警告。 On the page for that warning it states: 在該警告的頁面上,它指出:
You should consider suppressing the warning only if you're sure that you don't want to wait for the asynchronous call to complete and that the called method won't raise any exceptions . 僅當您確定不想等待非同步呼叫完成並且被呼叫的方法不會引發任何異常時 ,才應考慮禁止顯示警告。
I'm sure I don't want to wait for the call to complete; 我確定我不想等待通話結束。 I don't need to or have the time to. 我不需要或沒有時間。 But the call might raise exceptions. 但是通話可能會引發異常。
I've stumbled into this problem a few times and I'm sure it's a common problem which must have a common solution. 我已經幾次遇到這個問題,並且我確定這是一個常見的問題,必須有一個通用的解決方案。
How do I safely call an async method without awaiting the result? 如何安全地呼叫非同步方法而不等待結果?
For people suggesting that I just await the result, this is code that is responding to a web request on our web service (ASP.NET Web API). 對於建議我只是等待結果的人,這是響應我們Web服務(ASP.NET Web API)上的Web請求的程式碼。 Awaiting in a UI context keeps the UI thread free, but awaiting in a web request call will wait for the Task to finish before responding to the request, thereby increasing response times with no reason. 在UI上下文中等待將保持UI執行緒空閒,但是在Web請求呼叫中等待將等待Task完成,然後再響應請求,從而無故增加了響應時間。
參考:https://stackoom.com/question/138dm/如何在不等待的情況下安全地在C-中呼叫非同步方法
I guess the question arises, why would you need to do this? 我想這個問題出現了,爲什麼您需要這樣做? The reason for async
in C# 5.0 is so you can await a result. 在C#5.0中進行async
的原因是,您可以等待結果。 This method is not actually asynchronous, but simply called at a time so as not to interfere too much with the current thread. 此方法實際上不是非同步的,而是一次呼叫一次,以免對當前執行緒造成過多幹擾。
Perhaps it may be better to start a thread and leave it to finish on its own. 也許最好啓動一個執行緒並讓它自己完成。
You should first consider making GetStringData
an async
method and have it await
the task returned from MyAsyncMethod
. 您應該首先考慮使GetStringData
成爲async
方法,並使其await
MyAsyncMethod
返回的任務。
If you're absolutely sure that you don't need to handle exceptions from MyAsyncMethod
or know when it completes, then you can do this: 如果您完全確定不需要處理MyAsyncMethod
異常或知道它何時完成,則可以執行以下操作:
public string GetStringData()
{
var _ = MyAsyncMethod();
return "hello world";
}
BTW, this is not a "common problem". 順便說一句,這不是一個「常見問題」。 It's very rare to want to execute some code and not care whether it completes and not care whether it completes successfully. 這是非常罕見的要執行一些程式碼,並不在乎它是否完成並不在乎是否成功完成。
Update: 更新:
Since you're on ASP.NET and wanting to return early, you may find my blog post on the subject useful . 由於您使用的是ASP.NET並希望早日返回,因此您可能會發現關於該主題的部落格文章很有用 。 However, ASP.NET was not designed for this, and there's no guarantee that your code will run after the response is returned. 但是,ASP.NET並非爲此設計的,因此不能保證您的程式碼將在返迴響應後執行。 ASP.NET will do its best to let it run, but it can't guarantee it. ASP.NET將盡其所能使其執行,但不能保證。
So, this is a fine solution for something simple like tossing an event into a log where it doesn't really matter if you lose a few here and there. 所以,這是一些簡單的罰款解決方案一樣折騰的事件記錄到日誌:如果你失去了幾這裏和那裏它其實並不重要。 It's not a good solution for any kind of business-critical operations. 對於任何型別的關鍵業務操作來說,這都不是一個好的解決方案。 In those situations, you must adopt a more complex architecture, with a persistent way to save the operations (eg, Azure Queues, MSMQ) and a separate background process (eg, Azure Worker Role, Win32 Service) to process them. 在這種情況下,您必須採用更復雜的體系結構,並採用一種持久的方式來儲存操作(例如,Azure佇列,MSMQ),並使用單獨的後臺進程(例如,Azure Worker角色,Win32 Service)來處理它們。
If you want to get the exception "asynchronously", you could do: 如果要「非同步」獲取異常,則可以執行以下操作:
MyAsyncMethod().
ContinueWith(t => Console.WriteLine(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
This will allow you to deal with an exception on a thread other than the "main" thread. 這將使您可以處理「主」執行緒以外的執行緒上的異常。 This means you don't have to "wait" for the call to MyAsyncMethod()
from the thread that calls MyAsyncMethod
; 這意味着您不必「等待」來自呼叫MyAsyncMethod
的執行緒對MyAsyncMethod()
的呼叫; but, still allows you to do something with an exception--but only if an exception occurs. 但是,仍然允許您執行例外操作-但僅當發生例外時。
technically, you could do something similar with await
: 從技術上講,您可以使用await
做類似的事情:
try
{
await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
...which would be useful if you needed to specifically use try
/ catch
(or using
) but I find the ContinueWith
to be a little more explicit because you have to know what ConfigureAwait(false)
means. ...如果您需要專門使用try
/ catch
(或using
),這將很有using
但是我發現ContinueWith
更加明確,因爲您必須知道ConfigureAwait(false)
含義。
The answer by Peter Ritchie was what I wanted, and Stephen Cleary's article about returning early in ASP.NET was very helpful. Peter Ritchie的答案是我想要的, Stephen Cleary的有關在ASP.NET中儘早返回的文章非常有幫助。
As a more general problem however (not specific to an ASP.NET context) the following Console application demonstrates the usage and behavior of Peter's answer using Task.ContinueWith(...)
但是,作爲一個更普遍的問題(不是特定於ASP.NET上下文),以下控制檯應用程式使用Task.ContinueWith(...)
演示了彼得答案的用法和行爲。
static void Main(string[] args)
{
try
{
// output "hello world" as method returns early
Console.WriteLine(GetStringData());
}
catch
{
// Exception is NOT caught here
}
Console.ReadLine();
}
public static string GetStringData()
{
MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
return "hello world";
}
public static async Task MyAsyncMethod()
{
await Task.Run(() => { throw new Exception("thrown on background thread"); });
}
public static void OnMyAsyncMethodFailed(Task task)
{
Exception ex = task.Exception;
// Deal with exceptions here however you want
}
GetStringData()
returns early without awaiting MyAsyncMethod()
and exceptions thrown in MyAsyncMethod()
are dealt with in OnMyAsyncMethodFailed(Task task)
and not in the try
/ catch
around GetStringData()
GetStringData()
在不等待MyAsyncMethod()
情況下MyAsyncMethod()
返回,並且MyAsyncMethod()
中引發的異常在OnMyAsyncMethodFailed(Task task)
中處理,而不是在try
/ catch
GetStringData()
I end up with this solution : 我最終得到了這個解決方案:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
public string GetStringData()
{
// Run async, no warning, exception are catched
RunAsync(MyAsyncMethod());
return "hello world";
}
private void RunAsync(Task task)
{
task.ContinueWith(t =>
{
ILog log = ServiceLocator.Current.GetInstance<ILog>();
log.Error("Unexpected Error", t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}