我一直在閱讀一些關於異步/等待編程模型的文章,還有一些不太清楚的東西,我想分享一下我的困惑。ConfigureAwait和異步同步調用
假設我們有以下配置:
主,異步方法是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線程。我們知道上下文將被捕獲,當主方法完成時,上下文將被恢復。
如果我沒有錯,當第(一)異步方法被調用時,由於ConfigureAwait(false)
的UI上下文未捕獲並延續改爲推到一個線程池。但這並不保證會發生,因爲通話可能會立即完成。出於這個原因,我懷疑應該在所有內部異步方法上調用ConfigureAwait(false)
(在這種情況下,A和B)。這是正確的,還是運行時知道在看到第一次調用ConfigureAwait(false)
之後該怎麼做?
我的另一個擔心是關於兩個不好的做法(或考慮如此)和他們的副作用。
一個不好的做法似乎是,按照斯蒂芬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();});
沒錯,不過當然,如果他們能夠真正升級到'DoSomethingLittle_CAsync()'這將是更好。 –
@JonHanna確實。 – i3arnon
+1。我還補充說,如果你需要*,你應該只使用'Task.Run'。一般的經驗法則是,如果'DoSomethingLittle_C'會花費超過50ms,那麼只能在這裏使用它。 (另外,你錯過了最後一個'await')。 –