我知道,async/await 的问题是老生常谈,但是,如果有人帮助我解决以下问题,那就太好了:
先决条件: .网络框架。遗产。
ISomething
和,public void Run(ISomething input)
方法(类的实例是在控制器的构造函数中创建的),ISomething
这样的异步库将 _lib.Send(ISomething input)
对象发送到应用程序的另一部分。由于 public void Run(ISomething input)
是同步,并且库的方法是异步,因此 public void Run(ISomething input)
的引擎盖下有一个适配器 {1}}。
整个画面是这样的:
// _lib.Send(ISomething input) returns Task here and performs IO operation
public void Run(ISomething input)
{
RunAsync(() => _lib.Send(ISomething input));
}
private void RunAsync(Func<Task> input)
{
var result = Task.Run(() =>
{
input.Invoke();
}).ConfigureAwait(false);
result.GetAwaiter().GetResult();
}
我的问题是:
Task.Run()
中使用 async/await(如果可能,请解释)?_lib.Send(ISomething input)
? // 签名为:public Task Send(ISomething input);
答案 0 :(得分:1)
如何加入同步和异步?
最好的答案是“你没有”。在这种情况下,由于 Run
依赖于异步 _lib.Send
,那么 Run
应该更改为异步。然后调用 Run
的控制器操作应该更改为异步的。然后一切都是异步的,你不再有同步异步反模式。
然而,有时这个答案并不现实,因为更新遗留代码需要开发人员时间,而且通常服务器时间比开发人员时间便宜。 “go async all the way”的理想答案对于新代码是很好的建议,但对于遗留代码可能不现实。因此,旧版应用中有 a few hacks you can use to support both synchronous and asynchronous code。这里使用的 hack 是“线程池 hack”。
<块引用>有没有更好的方法来包装_lib.Send(ISomething input)
黑客的问题之一是它们并不适用于所有场景。从同步代码调用异步代码没有完美的万能解决方案,而且也不可能完美的万能解决方案。如果您必须保留旧的同步异步反模式,那么您只需要接受您需要使用的 hack 的限制。
在这种情况下,线程池黑客有一个限制,即被包装的代码 (_lib.Send
) 必须可以从裸线程池线程调用。由于这是一个旧的 ASP.NET 应用程序,这意味着代码不能依赖于 ASP.NET 上下文(包括 HttpContext.Current
)。根据 _lib.Send
的名称和描述,我希望它不依赖于上下文并且可以从任意线程调用,因此我希望线程池 hack 应该适用于该代码。
同样,这是基于这样的理解,即使用 Task RunAsync(ISomething input) => _lib.Send(input);
会更好,并且可以完全避免使用 Task.Run
包装器。您甚至可以保留旧的 void Run
实现,并根据需要逐渐将其余代码转换为使用 RunAsync
。这就是我推荐的。
是否应该在 Task.Run() 中使用 async/await(如果可能,请解释)?
是not necessary in this case。关键字应该用在任何重要的代码中,但由于委托只是调用一个方法,因此不需要 IMO。