实现即发即忘异步方法调用的正确方法
Correct way to implement fire and forget async method call
在我的项目中,我有一个发送电子邮件的网络调用,我不想等待响应,因为最有可能的电子邮件提供商已成功发送电子邮件。以下哪种方法比较好,有什么区别?
Method 1
:等待 SendAsync
并使用 Task.Run
public async Task<IActionResult> Index()
{
await SendAsync();
return View();
}
private Task SendAsync()
{
_ = Task.Run(async () =>
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
});
return Task.CompletedTask;
}
Method 2
: 不等待 SendAsync
public IActionResult Index()
{
SendAsync();
return View();
}
private async Task SendAsync()
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
}
这两种方法都适用于 Task.Delay(10000);
行中的等待
两者是等价的。尽管有很多人建议不要使用这种模式——如果你知道自己在做什么(比如记录日志、注意处理任务中的所有异常、注意后台任务在应用程序关闭期间正确运行等),来自我的体验使用这种模式实际上是可以的,至少在 .NET Core 中是这样。
在某些情况下,以持久的方式(使用数据库或队列)处理后台任务是不可能或不切实际或不可行的 (performance-wise)。
事实上,BackgroundService
的 Microsoft's implementation 的工作原理与此完全相同:
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it, this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
这里,ExecuteAsync
是一个没有await
的运行抽象任务(要被子类重写)。该任务存储在一个实例字段中,因此可以使用 CancellationToken
.
取消它
In my project, I have a network call to sending Email, I don't want to wait for the response, because the most likely Email provider sends the Emails successfully.
大多数电子邮件提供商的工作方式是让您将邮件发送到 队列,然后当他们处理该队列中的工作时才发送实际电子邮件。所以,发送到队列中是快速且非常可靠的。
所以这意味着您的 SendEmailAsync
或其他 API 应该非常快,并且不需要提前返回。由于 SendEmailAsync
实际上发送到 队列 ,它唯一代表的是“请接受我的 请求 来发送这封电子邮件”。
Which of the following methods is better and what is the difference?
都没有。
由于发送电子邮件只是一个队列写入,并且您不希望您的请求丢失,因此适当的方法是根本不使用即发即弃:
public async Task<IActionResult> Index()
{
await SendAsync();
return View();
}
private async Task SendAsync()
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
}
如果出于某种原因,您使用的电子邮件提供商不排队,那么您可以创建自己的队列(Azure 存储队列、Amazon SQS、RabbitMQ等)并更改 SendAsync
以将消息写入该队列。然后让一个单独的后台进程(Azure Function、Amazon Lambda 等)从该队列中读取并将电子邮件发送给电子邮件提供商。
在我的项目中,我有一个发送电子邮件的网络调用,我不想等待响应,因为最有可能的电子邮件提供商已成功发送电子邮件。以下哪种方法比较好,有什么区别?
Method 1
:等待 SendAsync
并使用 Task.Run
public async Task<IActionResult> Index()
{
await SendAsync();
return View();
}
private Task SendAsync()
{
_ = Task.Run(async () =>
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
});
return Task.CompletedTask;
}
Method 2
: 不等待 SendAsync
public IActionResult Index()
{
SendAsync();
return View();
}
private async Task SendAsync()
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
}
这两种方法都适用于 Task.Delay(10000);
行中的等待
两者是等价的。尽管有很多人建议不要使用这种模式——如果你知道自己在做什么(比如记录日志、注意处理任务中的所有异常、注意后台任务在应用程序关闭期间正确运行等),来自我的体验使用这种模式实际上是可以的,至少在 .NET Core 中是这样。 在某些情况下,以持久的方式(使用数据库或队列)处理后台任务是不可能或不切实际或不可行的 (performance-wise)。
事实上,BackgroundService
的 Microsoft's implementation 的工作原理与此完全相同:
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it, this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
这里,ExecuteAsync
是一个没有await
的运行抽象任务(要被子类重写)。该任务存储在一个实例字段中,因此可以使用 CancellationToken
.
In my project, I have a network call to sending Email, I don't want to wait for the response, because the most likely Email provider sends the Emails successfully.
大多数电子邮件提供商的工作方式是让您将邮件发送到 队列,然后当他们处理该队列中的工作时才发送实际电子邮件。所以,发送到队列中是快速且非常可靠的。
所以这意味着您的 SendEmailAsync
或其他 API 应该非常快,并且不需要提前返回。由于 SendEmailAsync
实际上发送到 队列 ,它唯一代表的是“请接受我的 请求 来发送这封电子邮件”。
Which of the following methods is better and what is the difference?
都没有。
由于发送电子邮件只是一个队列写入,并且您不希望您的请求丢失,因此适当的方法是根本不使用即发即弃:
public async Task<IActionResult> Index()
{
await SendAsync();
return View();
}
private async Task SendAsync()
{
_logger.LogInformation("Before");
await Task.Delay(10000); // Email send
_logger.LogInformation("After");
}
如果出于某种原因,您使用的电子邮件提供商不排队,那么您可以创建自己的队列(Azure 存储队列、Amazon SQS、RabbitMQ等)并更改 SendAsync
以将消息写入该队列。然后让一个单独的后台进程(Azure Function、Amazon Lambda 等)从该队列中读取并将电子邮件发送给电子邮件提供商。