有时我需要启动一个工作非常慢的异步作业。我不在乎那份工作是否成功,我需要继续处理我当前的话题。
有时我需要发送电子邮件或短信,但速度非常慢。我需要尽快回复网络客户端所以我不想
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
了怎么办? C# 中“即发即忘”的最佳实践是什么,只需调用异步方法而不等待其完成?
独立丢弃是避免此警告的最佳方法。
_ = Task.Run(() => _emailService.SendEmailAsync());
丢弃是虚拟变量,可用于忽略异步操作返回的任务对象。
https://learn.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
如果你真的只想解雇然后忘记。简单地不要调用使用等待。
// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);
// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);
如果你想稍后使用结果输出,它会变得更棘手。但如果真的着火了,忘了上面的方法应该行得通
取消令牌允许中断和取消程序。如果您正在使用取消令牌,您将需要在从直接检索到调用方法(一直到 Turtles 一直向下)的任何地方使用它。
我用
ConfigureAwait(false)
来防止死锁。 在这里了解更多信息
查看使用“Task.Factory.StartNew”的第二个答案我前段时间给出了这个答案。当时我没有意识到我当时的做法并不能确保完成。
如果你需要在你的函数中使用
async
你也可以使用一个 discard 变量 而不要使用 await。如果您有多个异步函数调用但您不需要等待所有调用,这也很有用。
public async function(){
var tmp = await asyncfunction();
...
_ = _httpClient.PutAsync(url, content);
...
}
正如 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());
确实会在新线程上工作。
这完全取决于您的 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.
}
这完全取决于您尝试调用的方法可以接受什么。我会查看文档以获得更多帮助。
我很好奇为什么没有人建议这样做。
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
//what ever code here...e.g.
DoSomething();
UpdateSomething();
}).Start();
它只是触发一个单独的线程。
Task.Run 与 async await 结合使用对我来说效果最好,即使没有丢弃。我认为它在自己的上下文中运行,并且在父上下文终止时不会终止,例如在具有依赖注入的 Azure 函数中。
Task.Run(async () => await _emailService.SendEmailAsync());