我一直在阅读有关异步/等待编程模型的一些文章,还有一些不太清楚的事情,我想分享我对这些的困惑。
假设我们有以下配置:
主要的异步方法是public async Task<bool> DoSomethingBigAsync(){...}
,其内部有3个其他方法,如下所示:
A)var a = await _someInstance.DoSomethingLittle_A_Async().ConfigureAwait(false);
B)var b = await _someInstance.DoSomethingLittle_B_Async();
C)var c = _someInstance.DoSomethingLittle_C();
例如,从UI线程调用main方法。我们知道将捕获上下文,并且当main方法完成时,将恢复上下文。
如果我没错,当第一个( A )异步方法被调用时,由于ConfigureAwait(false)
没有捕获UI上下文而是推送了延续从池中的线程。但这并不能保证会发生,因为呼叫可能会立即完成。出于这个原因,我怀疑,应该在所有内部异步方法上调用ConfigureAwait(false)
(在这种情况下, A 和 B )。这是正确的,还是运行时在看到第一次调用ConfigureAwait(false)
之后知道该怎么做?
我的另一个问题是关于两种不良做法(或被认为是这样)及其副作用。
根据Stephen Toub的文章,似乎有一种不好的做法:
为库中的同步方法公开异步包装器 很糟糕
因此,制作DoSomethingLittle_C()
方法的异步版本不是一个好主意。
其他不良做法似乎是:
不要混用阻塞和异步代码。
现在,看一下上面的代码,如果第一个ConfigureAwait(false)
可以保证继续被推送到线程池,那么在使用Task.StartNew(() => { c = _someInstance.DoSomethingLittle_C(); })
时我没有看到任何附加价值。
另一方面,如果ConfigureAwait(false)
永远不会保证将延续推送到线程池,那么我们可能会遇到问题,我们必须确保使用Task.StartNew(() => { c = _someInstance.DoSomethingLittle_C(); })
。
WRONG(?):
A)var a = await _someInstance.DoSomethingLittle_A_Async().ConfigureAwait(false);
B)var b = await _someInstance.DoSomethingLittle_B_Async();
C)var c = _someInstance.DoSomethingLittle_C();
CORRECT(?):
A)var a = await _someInstance.DoSomethingLittle_A_Async().ConfigureAwait(false);
B)var b = await _someInstance.DoSomethingLittle_B_Async().ConfigureAwait(false);
C)var c = Task.StartNew(() => {return _someInstance.DoSomethingLittle_C();});
答案 0 :(得分:8)
&#34;出于这个原因,我怀疑,应该在所有内部异步方法(本例中为A和B)上调用ConfigureAwait(false)。这是正确的,还是运行时在看到第一次调用ConfigureAwait(false)后知道该怎么做?&#34;
是。这应该。因为,正如您所说,之前的通话可能会同步完成,也可能是因为之前的通话可能会在未来发生变化,您也不想依赖它。
关于Task.Run
(首选Task.Factory.StartNew
),问题是是否在API的实施中使用它,答案几乎总是没有。
你的情况不同。如果你在UI线程上并且你有大量的工作(斯蒂芬克莱里的建议超过50毫秒)可以在后台完成,那么将工作卸载到ThreadPool
并没有错线程以保持UI响应。就像ConfigureAwait
一样,我不会依赖之前的调用将您转移到ThreadPool
,因为它也可以同步完成。
所以,正确的:
var a = await _someInstance.DoSomethingLittle_A_Async().ConfigureAwait(false);
var b = await _someInstance.DoSomethingLittle_B_Async().ConfigureAwait(false);
var c = await Task.Run(() => _someInstance.DoSomethingLittle_C()).ConfigureAwait(false);