多線程基礎和Thread類總結 Thread.Sleep() 和 Thread.SpinWait() Thread的Sleep和Join的區別

一、線程的和進程的關係以及優缺點

windows系統是一個多線程的操作系統。一個程序至少有一個進程,一個進程至少有一個線程。進程是線程的容器,一個C#客戶端程序開始於一個單獨的線程,CLR(公共語言運行庫)爲該進程創建了一個線程,該線程稱爲主線程。例如當我們創建一個C#控制檯程序,程序的入口是Main()函數,Main()函數是始於一個主線程的。它的功能主要 是產生新的線程和執行程序。C#是一門支持多線程的編程語言,通過Thread類創建子線程,引入using System.Threading命名空間。 

多線程的優點: 

1、 多線程可以提高CPU的利用率,因爲當一個線程處於等待狀態的時候,CPU會去執行另外的線程

2、 提高了CPU的利用率,就可以直接提高程序的整體執行速度

多線程的缺點:

1、線程開的越多,內存佔用越大

2、協調和管理代碼的難度加大,需要CPU時間跟蹤線程

3、線程之間對資源的共享可能會產生可不遇知的問題

 

二、前臺線程和後臺線程的區別和聯繫:

1、後臺線程不會阻止進程的終止。屬於某個進程的所有前臺線程都終止後,該進程就會被終止。所有剩餘的後臺線程都會停止且不會完成。

2、可以在任何時候將前臺線程修改爲後臺線程,方式是設置Thread.IsBackground 屬性。

3、不管是前臺線程還是後臺線程,如果線程內出現了異常,都會導致進程的終止。

4、託管線程池中的線程都是後臺線程,使用new Thread方式創建的線程默認都是前臺線程。

5、應用程序的主線程以及使用Thread構造的線程都默認爲前臺線程

6、線程池線程也就是使用 ThreadPool.QueueUserWorkItem()和Task工廠創建的線程都默認爲後臺線程

7、要注意的是,必須在調用Start方法之前設置線程的類型,否則一但線程運行,將無法改變其類型

三、線程優先級

由於windows上線程調用是(籠統的講)通過線程的優先級來實現的,那麼如果我們想使我們的程序能夠被儘量多的調度,就需要設置線程的優先級, 顯示在Thread類中,可以設置Priority屬性,以影響線程的基本優先級。Priority屬性需要一個ThreadPriority枚舉定義的值。     

四、什麼是線程安全:

線程安全是指在當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程纔可使用。不會出現數據不一致或者數據污染。

   線程有可能和其他線程共享一些資源,比如,內存,文件,數據庫等。當多個線程同時讀寫同一份共享資源的時候,可能會引起衝突。這時候,我們需要引入線程“同步”機制,即各位線程之間要有個先來後到,不能一窩蜂擠上去搶作一團。線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。

 

lock(this) 和lock(Obj)有什麼區別呢? 

lock(this) 鎖定 當前實例對象,如果有多個類實例的話,lock鎖定的只是當前類實例,對其它類實例無影響。所有不推薦使用。 
lock(typeof(Model))鎖定的是model類的所有實例。 
lock(obj)鎖定的對象是全局的私有化靜態變量。外部無法對該變量進行訪問。 
lock 確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區。如果其他線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。 
所以,lock的結果好不好,還是關鍵看鎖的誰,如果外邊能對這個誰進行修改,lock就失去了作用。所以一般情況下,使用私有的、靜態的並且是隻讀的對象。

總結:

1、lock的是必須是引用類型的對象,string類型除外。

2、lock推薦的做法是使用靜態的、只讀的、私有的對象。

3、保證lock的對象在外部無法修改纔有意義,如果lock的對象在外部改變了,對其他線程就會暢通無阻,失去了lock的意義。

     不能鎖定字符串,鎖定字符串尤其危險,因爲字符串被公共語言運行庫 (CLR)“暫留”。 這意味着整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應用程序域的所有線程中的該文本。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例。通常,最好避免鎖定 public 類型或鎖定不受應用程序控制的對象實例。例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因爲不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。而且lock(this)只對當前對象有效,如果多個對象之間就達不到同步的效果。lock(typeof(Class))與鎖定字符串一樣,範圍太廣了。

 

5.Monitor.Wait和Monitor()Pause()

Wait(object)方法:釋放對象上的鎖並阻止當前線程,直到它重新獲取該鎖,該線程進入等待隊列。
 Pulse方法:只有鎖的當前所有者可以使用 Pulse 向等待對象發出信號,當前擁有指定對象上的鎖的線程調用此方法以便向隊列中的下一個線程發出鎖的信號。接收到脈衝後,等待線程就被移動到就緒隊列中。在調用 Pulse 的線程釋放鎖後,就緒隊列中的下一個線程(不一定是接收到脈衝的線程)將獲得該鎖。
另外:

        Wait 和 Pulse 方法必須寫在 Monitor.Enter 和Moniter.Exit 之間。

Monitor.Wait是讓當前進程睡眠在臨界資源上並釋放獨佔鎖,它只是等待,並不退出,當等待結束,就要繼續執行剩下的代碼

 

 

線程池的概念

線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然後在創建線程後自動啓動這些任務。線程池線程都是後臺線程。每個線程都使用默認的堆棧大小,以默認的優先級運行,並處於多線程單元中。如果某個線程在託管代碼中空閒(如正在等待某個事件),則線程池將插入另一個輔助線程來使所有處理器保持繁忙。如果所有線程池線程都始終保持繁忙,但隊列中包含掛起的工作,則線程池將在一段時間後創建另一個輔助線程但線程的數目永遠不會超過最大值。超過最大值的線程可以排隊,但他們要等到其他線程完成後才啓動。

.線程池ThreadPool

相關概念:

線程池可以看做容納線程的容器;

一個應用程序最多隻能有一個線程池;

ThreadPool靜態類通過QueueUserWorkItem()方法將工作函數排入線程池;

每排入一個工作函數,就相當於請求創建一個線程;

線程池的作用:

線程池是爲突然大量爆發的線程設計的,通過有限的幾個固定線程爲大量的操作服務,減少了創建和銷燬線程所需的時間,從而提高效率。如果一個線程的時間非常長,就沒必要用線程池了(不是不能作長時間操作,而是不宜。),況且我們還不能控制線程池中線程的開始、掛起、和中止。

 

Thread類常用知識

關於Thread.Yield()和Thread.Sleep()

Thread.Sleep() 和 Thread.SpinWait()

 

前言:

應用程序應該讓線程等待而不是切換。

 

一:Thread.Sleep(1000);

Thread.Sleep()方法:是強制放棄CPU的時間片,然後重新和其他線程一起參與CPU的競爭。

 

二:Thread.SpinWait(1000);

Thread.SpinWait()方法:只是讓CPU去執行一段沒有用的代碼。當時間結束之後能馬上繼續執行,而不是重新參與CPU的競爭。

用Sleep()方法是會讓線程放棄CPU的使用權。

用SpinWait()方法是不會放棄CPU的使用權。

 

Thread的Sleep和Join的區別

我們的程序默認會有兩個線程,一個是主線程,一個是負責垃圾回收的線程。如果代碼不使用多線程,就只有主線程這一條幹道。
1.在主線程中調用Thread.Sleep(1000),表示主線程阻塞自己1秒。
2.在主線程中使用子線程調用Join()方法,表示子線程告訴主線程你需要阻塞一會,直到我完成任務。
兩者雖然都是阻塞主線程,但是,一個是主線程自己阻塞自己,另一個是子線程阻塞主線程。

 
        private void Test()
        {
            Thread.Sleep(1000);//此處主線程阻塞1秒

            var thread = new Thread(new ThreadStart(() =>
            {
                //模擬執行3秒
            }));
            thread.Start();
            thread.Join();//此處主線程阻塞3秒
}
複製代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章