1. 程式人生 > >前臺執行緒和後臺執行緒的區別、執行緒池的優缺點和使用場景

前臺執行緒和後臺執行緒的區別、執行緒池的優缺點和使用場景

1.執行緒的和程序的關係以及優缺點

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

多執行緒的優點 

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

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

多執行緒的缺點:

1、執行緒開的越多,記憶體佔用越大

2、協調和管理程式碼的難度加大,需要CPU時間跟蹤執行緒

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

 1.1 前臺執行緒和後臺執行緒

C#中的執行緒分為前臺執行緒和後臺執行緒,執行緒建立時不做設定預設是前臺執行緒。即執行緒屬性IsBackground=false。

區別以及如何使用:

    這兩者的區別就是:應用程式必須執行完所有的前臺執行緒才可以退出;而對於後臺執行緒,應用程式則可以不考慮其是否已經執行完畢而直接退出,所有的後臺執行緒在應用程式退出時都會自動結束。一般後臺執行緒用於處理時間較短的任務,如在一個Web伺服器中可以利用後臺執行緒來處理客戶端發過來的請求資訊。而前臺執行緒一般用於處理需要長時間等待的任務,如在Web伺服器中的監聽客戶端請求的程式。

3.什麼是執行緒安全:

執行緒安全是指在當一個執行緒訪問該類的某個資料時,進行保護,其他執行緒不能進行訪問直到該執行緒讀取完,其他執行緒才可使用。不會出現資料不一致或者資料汙染。

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

 4.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是讓當前程序睡眠在臨界資源上並釋放獨佔鎖,它只是等待,並不退出,當等待結束,就要繼續執行剩下的程式碼

6.執行緒池的概念

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

7.執行緒池ThreadPool

相關概念:

執行緒池可以看做容納執行緒的容器;

一個應用程式最多隻能有一個執行緒池;

ThreadPool靜態類通過QueueUserWorkItem()方法將工作函式排入執行緒池;

每排入一個工作函式,就相當於請求建立一個執行緒;

執行緒池的作用:

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

什麼時候使用ThreadPool?