await使用中的阻塞和并发(一)
好吧,不加點陳述不讓發(fā)首頁。那我們來陳述一下本篇提到的問題和對應(yīng)的方法。
在.NET4.5中,我們可以配合使用async和await兩個關(guān)鍵字,來以寫同步代碼的方式,實現(xiàn)異步的操作。
好處我目前看來有兩點:
1.不會阻塞UI線程。一旦UI線程不能及時響應(yīng),會極大的影響用戶體驗,這點在手機(jī)和平板的APP上尤為重要。
2.代碼簡潔。
- 相對基于event的異步方式,在多次回調(diào)的情況下(比如需要多次調(diào)web service,且后續(xù)調(diào)用基于前次調(diào)用的結(jié)果)特別明顯。可以將多個+=Completed方法合并到一起。
- 相對于Begin/End的異步方式,避免了N重且不能對齊的大括號。
? ? ?
在同一個方法里存在多個await的情況下,如后續(xù)Async方法無需等待之前的Aysnc方法返回的結(jié)果,會隱式的以并行方式來運行后面的Async方法。
值得注意的是錯誤的寫法會導(dǎo)致非預(yù)期的阻塞,下文會以簡單的例子來討論在使用await的情況下,怎樣實現(xiàn)多個Task的并發(fā)執(zhí)行。
我們這里先看幾個方法定義:
static async Task Delay3000Async(){await Task.Delay(3000);Console.WriteLine(3000);Console.WriteLine(DateTime.Now);}static async Task Delay2000Async(){await Task.Delay(2000);Console.WriteLine(2000);Console.WriteLine(DateTime.Now);}static async Task Delay1000Async(){await Task.Delay(1000);Console.WriteLine(1000);Console.WriteLine(DateTime.Now);} 作用很簡單,僅僅是起到延遲的作用。我們再來看如下寫法的調(diào)用
Console.WriteLine(DateTime.Now);new Action(async () =>{await Delay3000Async();await Delay2000Async();await Delay1000Async();})(); 結(jié)果如圖,可以看出3個await是線性執(zhí)行,第一個await會返回并阻止接下來的await后面的方法。這應(yīng)該不是我們想要的效果,畢竟后面的方法并不依賴第一個方法的執(zhí)行。
我們換一種寫法,再運行一次程序:
var task3 = Delay3000Async();var task2 = Delay2000Async();var task1 = Delay1000Async();new Action(async () =>{await task3;await task2;await task1;})(); 可以看到3個await后面的方法是并行執(zhí)行的。MSDN的解釋如下:
In an async method, tasks are started when they’re created.?The?Await?(Visual Basic) or?await?(C#) operator is applied to the task at the point in the method where processing can’t continue until the task finishes.?
However, you can separate creating the task from awaiting the task if your program has other work to accomplish that doesn’t depend on the completion of the task.
Between starting a task and awaiting it, you can start other tasks.?The additional tasks implicitly run in parallel, but no additional threads are created.
MSDN原文傳送門
到這里并沒有結(jié)束 ,后面還有一些奇怪的事情:
var tasks = new List<Task>{Delay3000Async(),Delay2000Async(),Delay1000Async()};tasks.ForEach(async _ => await _); 這個結(jié)果和上面是一樣的,可以并行執(zhí)行。這并不奇怪,我們僅僅是把Task放到一個List里,按照MSDN的說法,Task在被我們放進(jìn)List時就被創(chuàng)建,且并發(fā)執(zhí)行了。
那么我們再來一個List,這回放進(jìn)去的不是Task,而是Func<Task>:
var funcList = new List<Func<Task>>(){Delay3000Async,Delay2000Async,Delay1000Async};funcList.ForEach(async _ => await _()); 仍然可以并發(fā)執(zhí)行,看上去似乎沒什么問題,但是作為Func<Task>來存儲到List里,應(yīng)該是沒有被創(chuàng)建出來才對。為什么會能夠并發(fā)呢?
我們再來看最后一組寫法:
Func<Task> func3 = Delay3000Async;Func<Task> func2 = Delay2000Async;Func<Task> func1 = Delay1000Async;new Action(async () =>{await func3();await func2();await func1();})(); 意料之中的,以上的寫法并不能夠做到并發(fā)執(zhí)行。而是需要按順序執(zhí)行func3,func2和func1。這很好解釋,因為:?a task is awaited as soon as it’s created。我們在創(chuàng)建Task之后立即就要求阻塞并等待完成才進(jìn)行下一步。
寫到這里的時候?qū)ist<Func<Task>>的例子開始迷糊了。參考了Reflector反編譯的結(jié)果……我想說……沒看出來有什么區(qū)別……本篇先到這里。一旦琢磨出個所以然,我再發(fā)第二篇好了。
還恭請各位高人不吝賜教,多多提點。
?
補(bǔ)充:對List<Func<Task>>的那個例子,我懷疑是Foreach這個擴(kuò)展方法在偷偷做了優(yōu)化。故增加了如下的試驗:
static async Task TestForeach(){var funcList = new List<Func<Task>>(){Delay3000Async,Delay2000Async,Delay1000Async};foreach (var item in funcList){
//這里干了件蠢事,不要主動阻塞在這里,就可以并發(fā)了……await item();}} 試驗結(jié)果表明用foreach來寫的話,確實是做不到并行執(zhí)行的。那么就需要去看一下Foreach的背后到底發(fā)生了什么。我還要研究研究才能寫下一篇……
?哈哈哈哈,干了件蠢事情……
轉(zhuǎn)載于:https://www.cnblogs.com/leo9527/p/10340699.html
總結(jié)
以上是生活随笔為你收集整理的await使用中的阻塞和并发(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vv7多少钱啊?
- 下一篇: crontab修改默认编辑器