将异步委托传递给Task.Run? [复制]
Posted
技术标签:
【中文标题】将异步委托传递给Task.Run? [复制]【英文标题】:Passing an async delegate toTask.Run? [duplicate] 【发布时间】:2017-12-27 04:32:28 【问题描述】:假设我有一个函数Task UploadAsync(Stream stream)
将一些数据上传到远程机器。调用此函数将使 async 关键字传播到整个调用树。另一种方法是将它包装在 Task.Run 中,有点像 Fire and forgot 方式:
void DoUpload(Stream stream) Task.Run(async () => await UploadAsync(stream))
我从 Stephen Cleary 的blog 中读到说 Task.Run 应该只用于 CPU 绑定操作,显然上传数据块不受 CPU 限制。所以在这里,如果我在Task.Run
内调用await UploadAsync(stream)
似乎是错误的方式。
所以我的问题是,使用 Task.Run 包装和调用异步函数是一种不好的做法吗?
【问题讨论】:
您要等UploadAsync(stream)
完成吗?顺便说一句,Task.Run
将返回 Task
和 UploadAsync(stream)
- 将返回 Task
。那么,你有什么不同?为什么你需要Task.Run
?
您是想异步运行它然后忘记它,还是等到任务完成?在后一种情况下,只需调用Task task = UploadAsync(stream)
然后task.Wait()
等待它完成。
Another way is to wrap it in Task.Run, kind of Fire and forgot way:
如果您实际上只想触发它并忘记它,则根本没有理由将代码包装在Task.Run
中。这增加了很多开销而没有任何回报。也就是说,一开始就解雇并忘记类似的事情几乎肯定是错误的。
“使用 Task.Run 包装和调用异步函数是一种不好的做法吗?” - 什么是“不好的做法”主要是见仁见智。我同意其他cmets,这在您的情况下毫无意义。但它是有害还是错误?这充其量取决于您未提供的上下文,并且在大多数情况下,这只是一个意见问题。毕竟,如果你要让Task.Run()
任务忽略返回的任务,你还不如自己忽略它(这很容易)。
【参考方案1】:
调用此函数将使 async 关键字传播到整个调用树。
是的,这就是异步代码的重点。 If you call an asynchronous function synchronously, then it's not asynchronous.
最好的解决方案是采用异步代码,并使用await
调用它。如果由于某种原因这不可行,那么您可以使用one of the hacks in my brownfield async article。请注意,没有适用于所有场景的 hack。
例如,您可以使用 Thread Pool Hack:
Task.Run(() => UploadAsync(stream)).GetAwaiter().GetResult();
这会将异步代码包装到后台线程中(避免deadlock problems,但也阻止异步代码使用调用上下文),并阻塞等待结果(使用GetAwaiter().GetResult()
而不是Wait()
/@987654329 @ 以避免在错误情况下使用 AggregateException
包装器)。
但是,请注意exposing a synchronous wrapper for asynchronous methods is an antipattern。因此,提供以这种方式实现的Upload
方法将是一个糟糕的设计。
【讨论】:
【参考方案2】:Task.Run(async () => await UploadAsync(stream))
将返回一个您需要等待的任务,如果您关心等待它完成。在 Task.Run 中调用异步方法没有意义。
您可以在任务上调用.Wait(),这将阻塞线程直到请求完成。
异步调用确实有“感染”整个调用树的倾向,但我还没有找到任何合适的方法来避免这种情况。
【讨论】:
以上是关于将异步委托传递给Task.Run? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何将包装在 Promise 中的值传递给非异步函数? [复制]