應該這樣理解它
非同步,早期開發人員對它有很多誤解,認為不阻塞主執行緒就是非同步,更有認為不阻塞UI就是非同步,但非同步歸根結底和這兩個東西關係並不大,非同步的出現主要是為了提高執行緒的利用率,讓可用執行緒更高,而不是一個執行緒只做一件事,這件事沒有完成就不去做下面的事情,這是不正確的,執行緒應該被解放出來!事實上,你如果學過nodejs的話,對單執行緒非阻塞應該更清楚一些,它主要通過方法回撥來實現非同步的,只是在語法上和C#不太一樣。
說一下上面提到的誤解
誤解1:不阻塞主執行緒
如果不阻塞主執行緒的話,你只能開個新執行緒完成這個動作,像一些系統通知,它和主執行緒的工作流程沒有關係,如果開個新執行緒,與主執行緒並行執行,這並不是我們說的非同步,這只是多執行緒!它會增加執行緒的開支,使用不當,會影響系統的吞吐量!
誤解2:不阻塞UI
這就更屬於胡扯了,對於一個工作流來說,必須要按著1,2,3的順序去執行,如果是同步程式碼,它是一個執行緒從1執行到3,這個執行緒將一直被佔用!如果是非同步程式碼,它在執行到1時,執行緒被回收到池子,其它人可以使用,當1執行完成後,從執行緒池裡取出一個新的執行緒繼續執行,這叫非同步!C#的非同步進行友好,使用async,await就可以實現了!
實驗:檢視有效的執行緒剩餘數
// <summary> /// 執行緒非阻塞,執行緒利用率高 /// 執行緒await後可以去做其它事 /// 然後await後面方法結束後再申請新執行緒執行下面的程式碼 /// </summary> /// <returns></returns> [Route("~/do5")] public async Task<string> Do5() { var sw = new Stopwatch(); sw.Start(); await HttpHelper.Get("http://localhost:61699/do1"); await HttpHelper.Get("http://localhost:61699/do2"); await HttpHelper.Get("http://localhost:61699/do3"); await HttpHelper.Get("http://localhost:61699/do4"); sw.Stop(); int workerThreads, completionPortThreads; ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); return $"max threads:{workerThreads},completionPortThreads:{completionPortThreads},timer:{sw.ElapsedMilliseconds.ToString()}"; }
我們來看它的I/O執行緒剩餘,多重新整理幾次,一直維持在32766和32765之間
而如果使用同步程式碼,結果就完成不一樣了,執行緒剩餘各位可以看下面
執行緒ID在每個await時是不同的
下面是一個更明顯的測試,依次執行多個await,然後獲取當前執行緒的ID,它們在非同步環境下,有可能是不同的,因為每次都要從池子裡拿新的執行緒!
await HttpHelper.Get("http://localhost:61699/do1"); str.Append($"step1:{Thread.CurrentThread.ManagedThreadId}"); await HttpHelper.Get("http://localhost:61699/do2"); str.Append($"step2:{Thread.CurrentThread.ManagedThreadId}"); await HttpHelper.Get("http://localhost:61699/do3"); str.Append($"step3:{Thread.CurrentThread.ManagedThreadId}"); await HttpHelper.Get("http://localhost:61699/do4"); str.Append($"step4:{Thread.CurrentThread.ManagedThreadId}");
通過這篇文章,我們應該真正理解非同步這個概念了吧,記住,非同步主要為了提高執行緒利用率,從而提高系統的吞吐量的,它與並行,主執行緒阻塞很有直接關係,也不是它所研究的重點,這個大家一定要記住!