如何在不等待的情况下安全地调用 C# 中的异步方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15522900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to safely call an async method in C# without await
提问by George Powell
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. On the page for that warning it states:
在MyAsyncMethod()
不等待的情况下调用会导致Visual Studio 中的“因为未等待此调用,当前方法在调用完成之前继续运行”警告。在该警告的页面上,它指出:
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 mightraise 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?
如何在不等待结果的情况下安全地调用异步方法?
Update:
更新:
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). 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.
对于建议我只是等待结果的人,这是响应我们的 Web 服务(ASP.NET Web API)上的 Web 请求的代码。在 UI 上下文中等待使 UI 线程保持空闲,但在 Web 请求调用中等待将在响应请求之前等待任务完成,从而无缘无故地增加响应时间。
采纳答案by Peter Ritchie
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 toMyAsyncMethod()
from the thread that calls MyAsyncMethod
; but, still allows you to do something with an exception--but only if an exception occurs.
这将允许您处理“主”线程以外的线程上的异常。这意味着您不必“等待”MyAsyncMethod()
来自调用的线程的调用MyAsyncMethod
;但是,仍然允许您在异常情况下执行某些操作——但前提是发生异常时。
Update:
更新:
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
),这将很有用,但我发现ContinueWith
更明确一点,因为您必须知道什么ConfigureAwait(false)
意思。
回答by rhughes
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. This method is not actually asynchronous, but simply called at a time so as not to interfere too much with the current thread.
我想问题来了,你为什么需要这样做?async
在 C# 5.0 中的原因是这样您就可以等待结果。这个方法实际上并不是异步的,而是简单的一次调用,以免对当前线程产生过多的干扰。
Perhaps it may be better to start a thread and leave it to finish on its own.
也许最好启动一个线程并让它自行完成。
回答by Stephen Cleary
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
orknow 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 andnot 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. However, ASP.NET was not designed for this, and there's no guaranteethat your code will run after the response is returned. ASP.NET will do its best to let it run, but it can't guarantee it.
由于您使用的是 ASP.NET 并希望早日返回,您可能会发现我关于该主题的博客文章很有用。但是,ASP.NET 并不是为此而设计的,并且不能保证您的代码会在返回响应后运行。ASP.NET 会尽最大努力让它运行,但它不能保证它。
So, this is a fine solution for something simple like tossing an event into a log where it doesn't reallymatter 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 mustadopt a more complex architecture, with a persistent way to save the operations (e.g., Azure Queues, MSMQ) and a separate background process (e.g., Azure Worker Role, Win32 Service) to process them.
所以,这是一些简单的罚款解决方案一样折腾事件写入日志在它不真的,如果你失去了几这里有关系。对于任何类型的关键业务操作来说,这都不是一个好的解决方案。在这些情况下,您必须采用更复杂的体系结构,以持久方式保存操作(例如 Azure 队列、MSMQ)和单独的后台进程(例如 Azure Worker Role、Win32 Service)来处理它们。
回答by George Powell
The answer by Peter Ritchie was what I wanted, and Stephen Cleary's articleabout 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 上下文),以下控制台应用程序演示了 Peter 的答案的用法和行为,使用 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 notin the try
/catch
around GetStringData()
GetStringData()
返回早期没有等待MyAsyncMethod()
和异常抛出MyAsyncMethod()
在处理OnMyAsyncMethodFailed(Task task)
和未在try
/catch
左右GetStringData()
回答by Filimindji
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);
}
回答by haimb
On technologies with message loops (not sure if ASP is one of them), you can block the loop and process messages until the task is over, and use ContinueWith to unblock the code:
在带有消息循环的技术上(不确定 ASP 是否是其中之一),您可以阻塞循环并处理消息,直到任务结束,然后使用 ContinueWith 解除阻塞代码:
public void WaitForTask(Task task)
{
DispatcherFrame frame = new DispatcherFrame();
task.ContinueWith(t => frame.Continue = false));
Dispatcher.PushFrame(frame);
}
This approach is similar to blocking on ShowDialog and still keeping the UI responsive.
这种方法类似于在 ShowDialog 上阻塞并仍然保持 UI 响应。
回答by wast
This is called fire and forget, and there is an extensionfor that.
这被称为fire and forget,并且有一个扩展。
Consumes a task and doesn't do anything with it. Useful for fire-and-forget calls to async methods within async methods.
消耗一个任务并且不做任何事情。对于异步方法中对异步方法的即发即忘调用很有用。
Install nuget package.
安装nuget 包。
Use:
用:
MyAsyncMethod().Forget();
回答by Ernesto Garcia
The solution is start the HttpClient into another execution task without sincronization context:
解决方案是将 HttpClient 启动到另一个没有 sincronization 上下文的执行任务中:
var submit = httpClient.PostAsync(uri, new StringContent(body, Encoding.UTF8,"application/json"));
var t = Task.Run(() => submit.ConfigureAwait(false));
await t.ConfigureAwait(false);
回答by CharithJ
If you really wants to do this. Just to address "Call an async method in C# without await", you can execute the async method inside a Task.Run
. This approach will wait until MyAsyncMethod
finish.
如果你真的想这样做。只是为了解决“在没有等待的情况下在 C# 中调用异步方法”,您可以在Task.Run
. 这种方法将等到MyAsyncMethod
完成。
public string GetStringData()
{
Task.Run(()=> MyAsyncMethod()).Result;
return "hello world";
}
await
asynchronously unwraps the Result
of your task, whereas just using Resultwould block until the task had completed.
await
异步解开Result
你的任务,而只使用Result会阻塞直到任务完成。
回答by TarmoPikaro
Typically async method returns Task class. If you use Wait()
method or Result
property and code throws exception - exception type gets wrapped up into AggregateException
- then you need to query Exception.InnerException
to locate correct exception.
通常 async 方法返回 Task 类。如果您使用Wait()
方法或Result
属性并且代码抛出异常 - 异常类型被包装成AggregateException
- 那么您需要查询Exception.InnerException
以定位正确的异常。
But it's also possible to use .GetAwaiter().GetResult()
instead -
it will also wait async task, but will not wrap exception.
但也可以改为使用.GetAwaiter().GetResult()
- 它也会等待异步任务,但不会包装异常。
So here is short example:
所以这是一个简短的例子:
public async Task MyMethodAsync()
{
}
public string GetStringData()
{
MyMethodAsync().GetAwaiter().GetResult();
return "test";
}
You might want also to be able to return some parameter from async function - that can be achieved by providing extra Action<return type>
into async function, for example like this:
您可能还希望能够从 async 函数返回一些参数 - 这可以通过向Action<return type>
async 函数提供额外的内容来实现,例如像这样:
public string GetStringData()
{
return MyMethodWithReturnParameterAsync().GetAwaiter().GetResult();
}
public async Task<String> MyMethodWithReturnParameterAsync()
{
return "test";
}
Please note that async methods typically have ASync
suffix naming, just to be able to avoid collision between sync functions with same name. (E.g. FileStream.ReadAsync
) - I have updated function names to follow this recommendation.
请注意,异步方法通常具有ASync
后缀命名,只是为了能够避免同名同步函数之间发生冲突。(例如FileStream.ReadAsync
) - 我更新了函数名称以遵循此建议。