2015-07-28 144 views
4

我一直在閱讀一些關於異步/等待編程模型的文章,還有一些不太清楚的東西,我想分享一下我的困惑。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)(在這種情況下,AB)。這是正確的,還是運行時知道在看到第一次調用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();});

回答

8

「出於這個原因,我懷疑,即ConfigureAwait(假)應在所有的內異步方法被調用(A和B在這是正確的,還是運行時知道在第一次調用ConfigureAwait(false)後知道該怎麼做?)

是的。這應該。因爲,正如你所說,以前的通話可能會同步完成,也是因爲以前的通話可能會在將來發生變化,您不想依賴它。

關於Task.Run(優先於Task.Factory.StartNew),問題是在API的實現中是否使用它,答案几乎總是否定的。

您的情況有所不同。如果你在UI線程上並且你有大量的工作(Stephen Cleary的建議超過50ms)可以在後臺完成,那麼將該工作卸載到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); 
+0

沒錯,不過當然,如果他們能夠真正升級到'DoSomethingLittle_CAsync()'這將是更好。 –

+0

@JonHanna確實。 – i3arnon

+2

+1。我還補充說,如果你需要*,你應該只使用'Task.Run'。一般的經驗法則是,如果'DoSomethingLittle_C'會花費超過50ms,那麼只能在這裏使用它。 (另外,你錯過了最後一個'await')。 –