非同步與執行緒阻塞

張佔嶺發表於2017-11-08

返回目錄

應該這樣理解它

非同步,早期開發人員對它有很多誤解,認為不阻塞主執行緒就是非同步,更有認為不阻塞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}");

通過這篇文章,我們應該真正理解非同步這個概念了吧,記住,非同步主要為了提高執行緒利用率,從而提高系統的吞吐量的,它與並行,主執行緒阻塞很有直接關係,也不是它所研究的重點,這個大家一定要記住!

返回目錄

相關文章