程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 自以為是的多線程(一),自以為是多線程(

自以為是的多線程(一),自以為是多線程(

編輯:C#入門知識

自以為是的多線程(一),自以為是多線程(


    多線程在web開發裡面其實應用場景並不多,而且應用到多線程的場景也大多都是一些比較簡單的場景,基本上大多都可以用Task代替,所以很多web開發人員對多線程的理解非常的淺薄,也就導致了會出現很多不可預計的bug,然後又因此寫了一大堆邏輯來繞來繞去,所以我想談談多線程,試圖做到高屋建瓴,給大家一個比較開闊的視野。

    這一篇先從性能角度來說,下一篇從線程安全角度來講解。

    先解釋一下,為什麼線程不是越多越好:

    談到多線程必須要談到cpu,一個單核心cpu在同一個時間內,只能執行一個線程(受限於目前技術),對於超線程cpu(intel專利,使用了超線程技術的cpu,容許一個核同時執行兩個線程,在windows操作系統裡面也會顯示有兩個核心),我們都理解成是雙核心cpu。

    而對於每個線程,都一定包含以下幾個要素:

    1、線程內核對象,系統為每個線程創建的,包括上下文(context)等。

    2、線程環境塊,是一塊內存,包含線程的異常處理鏈首。

    3、用戶模式棧、內核模式棧。

  4、DLL線程鏈接和線程分離通知

    這個是我們在創建一個新線程時,系統在為線程初始化時需要創建的東西。

    而前面又提到了,一個單核CPU同一時間內只能跑一個線程,那麼當一個CPU正在跑一個線程的時候,這個線程的很多數據已經存到了CPU的高速緩存中,這個時候發生了切換,我們需要切換到另外一個線程上面去執行,那這個線程執行的可能是另外的代碼,需要讀取另外的數據,這個時候CPU就需要重新去內存裡面去取數據,來填充這個高速緩存。而CPU去內存裡面取數據的這個過程,相比於去高速緩存裡面取數據來說,是很慢的,這也就是說,當我們頻繁切換線程的話,CPU就需要做很多額外的事情。這也就是說,當只有單核CPU時,同樣的功能,一個線程肯定比多個線程更快。這裡有一個矛盾,就是系統不可能只有一個線程,系統還牽涉到很多自己的系統線程,以及其它的應用程序的線程,那系統在做線程切換(系統決定調用哪個線程)的時候,會牽涉到一個線程優先級的問題,而這個調度方法(相對合理智能的一個算法)是我們不可控的,所以說當你的程序有多個線程的情況下,在做線程切換的時候,切換到你的線程上面的概率會變大,所以也不能絕對的說一個線程就一定比多個線程快,這裡只是一個大概的相對理論情況。

    既然是這種情況,那我們在寫代碼的時候,就應該盡可能的避免出現線程切換,而讓CPU盡可能的執行該線程。可是在什麼情況下會容易出現線程切換(以下所有的線程切換都是指的相對或是可能,因為線程調度不可控)。

    比如我有以下代碼:

public static string ReadText(string path)
        {
            string text = "";
            if (File.Exists(path))
            {
                using (Stream fs = File.Open(path, FileMode.Open))
                {
                    using (StreamReader sr = new StreamReader(fs))
                    {
                        text = sr.ReadToEnd();
                        sr.Close();
                    }
                }
            }
            return text;
        }

    這個是很常見的IO讀取,這個時候會給IO線程發出一個請求,然後等待IO的響應,在等待的過程中,系統會把這個線程鎖定(這是個很棒的設計),讓CPU去做其它事情,等到執行響應完畢以後,再喚醒該線程。同理,在做數據庫的讀取以及一些其他的IO請求時,都會這樣。假如當有一個用戶請求時,我們就會執行這樣一段代碼,那當有不斷多的用戶請求時,系統就會創建不斷多的線程(創建線程的本身開銷就很昂貴),而當IO讀取完響應的時候,又會有不斷多的線程逐漸被喚醒,系統這個時候就又會疲於線程切換,你就會發現性能開始巨降。

同理,當我有以下代碼時:

lock(object){
    ...
}

當多個線程在執行這段代碼的時候,就會出現很有意思的情況。

假如1-10,一共有10個線程需要執行到這段代碼,假如cpu是2個(分別為A、B,分別執行的是1、2),當1執行的時候,2被鎖定,這個時候B CPU就開始做線程切換,調度3-10中的任意一個繼續執行,如果這段代碼較長,A不一定能在短時間內執行完,那就會出現,調度一個鎖一個,繼續切換,再調度,再鎖定,再切換的一個循環,一直到所有線程都被鎖定為止。

當1執行完畢的時候,這個時候喚醒所有被鎖定的線程,然後重新分配給A、B兩個CPU,然後當A又執行到這裡的時候,B又會出現剛剛再調度、再鎖定,再切換這樣的一個循環情況。所以在多線程開發的時候,盡可能的避免共享資源的出現。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved