当您调用没有 await 关键字的异步方法时,究竟会发生什么?



我有一个Web服务器,并且有定期的作业合并和发送记录(大量请求日志)。

Task.Run(() =>
{
while (true)
{
try
{
MergeAndPutRecords();
}
catch (Exception ex)
{
Logger.Error(ex);
}
}
});

MergeAndPutRecords函数中,有代码合并记录和异步函数返回任务发送记录。(实际上它是Amazon Kinesis Firehose的PutRecordBatchAsync。

那么如果我在没有 await 关键字的情况下调用该函数会发生什么?函数是否在单独的线程上运行?这里说不是。那么什么是返回任务意味着什么?这里说没有 await 关键字的异步方法意味着

  1. 在当前线程上启动异步方法。忽略所有结果(包括异常)。

那么我的定期作业和 PutRecordBatchAsync 是同时处理的?我知道异步和并发是不同的。但是没有 await 关键字,它们在同一个线程中。哪一个会先执行?我很困惑...

将有大量记录需要实时合并和发送。所以我认为它必须同时执行。

那么我的定期作业和 PutRecordBatchAsync 是同时处理的?

使用任务 API,您可以确保它们并发执行(使用线程池),但您需要了解内存并发操作与基于 IO 的并发性之间的区别。

虽然内存中并发确实有利于使用 Tasks,但 IO 调用一旦执行就根本不需要线程,因为它依赖于硬件并发,如果它使用线程,它所做的一切都会等待 IO 调用返回,从而浪费宝贵的系统资源并降低系统可扩展性

您的情况是基于 IO 的并发,当您调用基于远程/网络的 API 时,异步等待在这里有什么帮助?

真正的异步操作将释放线程上下文,在 Windows 上它将使用 IO 完成端口(排队机制)来执行异步调用,而调用线程用于调度其他类似的调用,它只需要返回 IO 调用时的线程上下文来提供响应,为此, 如果不是 UI 调用,则使用ConfigureAwait(false),以便任何线程上下文都可用于传递响应。

如果你不使用 await 和异步怎么办?

本应为异步的调用将变为同步,并会立即影响系统可伸缩性,因为线程现在被阻塞,对于长时间运行的 IO 操作来说甚至更糟。你有没有看到JavaScript框架总是对服务器API进行AJAX(异步)调用,因此可以在不阻塞浏览器线程的情况下做更多的工作。

通常,对于内存处理,您将创建一定数量的任务并使用集合的Task.WaitAllParallel.ForEach来处理它们,对于异步处理,理想情况下建议不要在任何地方Task.Run,最好必须从入口点Async,就像在 MVC 的情况下一样, 控制器可以是异步的。使用代表性任务将多个呼叫组合在一起Task.WhenAll然后等待。即使您在代码中使用Task.Run,也可以使用async lambda执行异步调用

总结:

必须对异步调用使用await,否则他们async关键字在该上下文中是无用的,是的,await将在执行继续之前等待 IO 调用返回,尽管在此过程中没有线程被阻塞

如果一个方法返回一个Task,那么在某个时候观察该Task的结果始终是最佳做法。它可能是声明的返回值,也可能是异常。如果要在方法继续之前观察该任务,建议await

在这种情况下,您可能应该观察PutRecordBatchAsync返回的Task的结果。您将想知道呼叫是否因任何原因失败,因为这可能表明您的记录未存储!

在您给出的示例代码中,您会发现在上一个调用完成之前对MergeAndPutRecords进行了后续调用。你确定这是有意的吗?

最新更新