C#如何在不等待完成的情况下启动异步方法?
Posted
技术标签:
【中文标题】C#如何在不等待完成的情况下启动异步方法?【英文标题】:C# How to start an async method without await its complete? 【发布时间】:2019-04-10 12:33:15 【问题描述】:有时我需要启动一个运行速度很慢的异步作业。我不在乎那份工作是否成功,我需要继续处理我当前的线程。
就像有时我需要发送一封电子邮件或短信,它的工作速度很慢。我需要尽快回复网络客户端,所以我不想await
它。
我用谷歌搜索了这个问题,有些文章建议我这样写:
// This method has to be async
public async Task<Response> SomeHTTPAction()
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Run(() => _emailService.SendEmailAsync());
return MyRespond();
或者像这样:
// This method has to be async
public async Task<Response> SomeHTTPAction()
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
会有一个警告说:before the call is completed. Consider applying the 'await' operator to the result of the call.
如果我真的await
ed 怎么办? C# 中“触发并忘记”的最佳实践是什么,只需调用异步方法而不等待其完成?
【问题讨论】:
只是不要将函数标记为异步,所以函数不应该期望它的结果被等待? How to safely call an async method in C# without await的可能重复 更好的方法是让这些操作在进程之外运行,即发布到像 rabbitmq 这样的队列,让订阅者接收这些消息,然后发送电子邮件/短信 到目前为止你发现了什么信息? “C# async fire and forget”的快速谷歌显示有很多可用的资源.. 这真的不是设计的问题吗?在您知道操作是否完成之前返回MyRespond
有什么意义?
【参考方案1】:
如果你真的只是想开火然后忘记。根本不要调用使用等待。
// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);
// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);
如果您想稍后使用结果输出,它会变得更加棘手。但如果它真的很火,忘记上面应该工作
取消标记允许中断和取消过程。如果您使用的是 Cancellation 令牌,您将需要在从检索到调用方法的所有地方使用它(一直到 Turtles)。
我使用ConfigureAwait(false)
来防止死锁。 Here了解更多信息
【讨论】:
@MickyD 主要是因为 OP 如何拥有它,但是有了这个,您可以稍后使用 .GetAwaiter().GetResult() 检索该值 我们为什么使用cancellationToken以及如何使用?【参考方案2】:独立丢弃是避免此警告的最佳方法。
_ = Task.Run(() => _emailService.SendEmailAsync());
丢弃是虚拟变量,可用于忽略异步操作返回的任务对象。
https://docs.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
【讨论】:
这个答案是正确的。我投反对票只是因为它与 MrMaavin 的 earlier answer 非常相似。【参考方案3】:正如 Amadan 在评论中所说,您需要从函数中删除异步。那么它将停止给你警告。
// This method has to be async
public Response SomeHTTPAction()
// Some logic...
// ...
// Send an Email but don't care if it successfully sent.
Task.Factory.StartNew(() => _emailService.SendEmailAsync());
return MyRespond();
而Task.Factory.StartNew(() => _emailService.SendEmailAsync());
确实可以在新线程上工作。
【讨论】:
【参考方案4】:如果您需要在函数中使用async
,您也可以使用丢弃变量,不要使用等待。如果您有多个异步函数调用但您不需要等待所有它们,这也很有用。
public async function()
var tmp = await asyncfunction();
...
_ = _httpClient.PutAsync(url, content);
...
【讨论】:
【参考方案5】:我很好奇为什么不建议这样做。
new Thread(() =>
Thread.CurrentThread.IsBackground = true;
//what ever code here...e.g.
DoSomething();
UpdateSomething();
).Start();
它只是触发一个单独的线程。
【讨论】:
这将创建一个新线程并在该新线程上运行方法,这非常昂贵/消耗资源。不是相关问题的答案。【参考方案6】:这完全取决于您的 Async 方法接受什么。通常它会接受一个也举办活动的“特殊”课程。您可以将回调方法订阅到该事件并将其与方法一起传递。完成后,将调用您的回调方法。
一个例子(对于套接字)是:
public void CreateSocket()
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
sockAsync.Completed += SockAsync_Completed;
s.ConnectAsync(sockAsync);
private void SockAsync_Completed(object sender, SocketAsyncEventArgs e)
//Do stuff with your callback object.
这完全取决于您尝试调用的方法可以接受什么。我会专门查看文档以获取更多帮助。
【讨论】:
以上是关于C#如何在不等待完成的情况下启动异步方法?的主要内容,如果未能解决你的问题,请参考以下文章