線程同步

Posted on 2011-05-06 20:11 明天陪你看海 閱讀(110) 評論(0) 編輯 收藏
在應用程序中使用多個線程的一個好處是每個線程都可以異步執行。對於Windows應用程序,耗時的任務可以在後臺執行,而使應用程序窗口和控件保持響應。對於服務器應用程序,多線程處理提供了用不同線程處理每個傳入請求的能力。否則,在完全滿足前一個請求之前,將無法處理每個新請求。然而,線程的異步性意味着必須協調對資源(如文件句柄、網絡連接和內存)的訪問。否則,兩個或更多的線程可能在同一時間訪問相同的資源,而每個線程都不知道其他線程的操作,結果將產生不可預知的數據損壞。
線程同步是指併發線程高效、有序的訪問共享資源所採用的技術,所謂同步,是指某一時刻只有一個線程可以訪問資源,只有當資源所有者主自動放棄了代碼或資源的所有權時,其他線程纔可以使用這些資源。
線程同步可以分別使用C#中的lock關鍵字、Monitor類、Interlocked類和Mutex類實現,下面對這4種實現方法進行詳細介紹。
1.使用C#中的lock關鍵字實現線程同步
lock關鍵字可以用來確保代碼塊完成運行,而不會被其他線程中斷,它是通過在代碼塊運行期間爲給定對象獲取互斥鎖來實現的。
lock語句以關鍵字lock開頭,它有一個作爲參數的對象,在該參數的後面還有一個一次只能由一個線程執行的代碼塊。Lock語句語法格式如下。

Object thisLock = new Object();
lock (thisLock)
{
    //要運行的代碼塊
}

提供給lock語句的參數必須爲基於引用類型的對象,該對象用來定義鎖的範圍。嚴格來說,提供給lock語句的參數只是用來唯一標識由多個線程共享的資源,所以它可以是任意類實例,然而,實際上,此參數通常表示需要進行線程同步的資源。例如,如果一個容器對象將被多個線程使用,則可以將該容器傳遞給lock語句,而lock語句中的代碼塊將訪問該容器。只要其他線程在訪問該容器前先鎖定該容器,則對該對象的訪問將是安全同步的。
通常,最好避免鎖定public類型或不受應用程序控制的對象實例。例如,如果該實例可以被公開訪問,則lock(this)可能會有問題,因爲不受控制的代碼也可能會鎖定該對象,這將可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題,鎖定字符串尤其危險,因爲字符串被公共語言運行庫(CLR)“暫留”,這意味着整個程序中任何給定字符串都只有一個實例,因此,只要在應用程序進程中的任何具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例,因此,最好鎖定不會被暫留的私有或受保護成員。
 說明:事實上lock語句是用Monitor類來實現的,它等效於try/finally語句塊,使用lock關鍵字通常比直接使用Monitor類更可取,一方面是因爲lock更簡潔,另一方面是因爲lock確保了即使受保護的代碼引發異常,也可以釋放基礎監視器。這是通過finally關鍵字來實現的,無論是否引發異常它都執行關聯的代碼塊。
例如創建一個控制檯應用程序,其中自定義了一個LockThread方法,該方法中使用lock關鍵字鎖定當前線程,然後在Main方法中通過Program的類對象調用LockThread自定義方法。代碼如下。

static void Main(string[] args)
{
    Program myProgram = new Program();//實例化類對象
    myProgram.LockThread();//調用鎖定線程方法
}
void LockThread()
{
    lock (this) //鎖定當前線程,以實現同步
    {
        Console.WriteLine("鎖定線程以實現線程同步");
    }
}
2.使用Monitor類實現線程同步
Monitor類提供了同步對對象的訪問機制,它通過向單個線程授予對象鎖來控制對對象的訪問,對象鎖提供限制訪問代碼塊(通常稱爲臨界區)的能力。當一個線程擁有對象鎖時,其他任何線程都不能獲取該鎖。
Monitor類的主要功能如下。
 它根據需要與某個對象相關聯。
 它是未綁定的,也就是說可以直接從任何上下文調用它。
 不能創建Monitor類的實例。
Monitor類的常用方法及說明如表1所示。
表1  Monitor類的常用方法及說明

方法

說明

Enter

在指定對象上獲取排他鎖

Exit

釋放指定對象上的排他鎖

Pulse

通知等待隊列中的線程鎖定對象狀態的更改

PulseAll

通知所有的等待線程對象狀態的更改

TryEnter

試圖獲取指定對象的排他鎖

Wait

釋放對象上的鎖並阻止當前線程,直到它重新獲取該鎖


 注意:使用Monitor類鎖定的是對象(即引用類型)而不是值類型。
例  創建一個控制檯應用程序,其中自定義了一個LockThread方法,該方法中首先使用Monitor類的Enter方法鎖定當前線程,然後再調用Monitor類的Exit方法釋放當前線程,最後在Main方法中通過Program的類對象調用LockThread自定義方法。代碼如下。

static void Main(string[] args)
{
    Program myProgram = new Program();//實例化類對象
    myProgram.LockThread();//調用鎖定線程方法
}
void LockThread()
{
    Monitor.Enter(this); //鎖定當前線程
    Console.WriteLine("鎖定線程以實現線程同步");
    Monitor.Exit(this); //釋放當前線程
}
3.使用Mutex類實現線程同步
當兩個或更多線程需要同時訪問一個共享資源時,系統需要使用同步機制來確保一次只有一個線程使用該資源。Mutex類是同步基元,它只向一個線程授予對共享資源的獨佔訪問權。如果一個線程獲取了互斥體,則要獲取該互斥體的第二個線程將被掛起,直到第一個線程釋放該互斥體。Mutex類與監視器類似,它防止多個線程在某一時間同時執行某個代碼塊,然而與監視器不同的是,Mutex類可以用來使跨進程的線程同步。
可以使用WaitHandle.WaitOne方法請求互斥體的所屬權,擁有互斥體的線程可以在對WaitOne方法的重複調用中請求相同的互斥體而不會阻止其執行,但線程必須調用同樣多次數的ReleaseMutex方法以釋放互斥體的所屬權。Mutex類強制線程標識,因此互斥體只能由獲得它的線程釋放。
當用於進程間同步時,Mutex稱爲“命名Mutex”,因爲它將用於另一個應用程序,因此它不能通過全局變量或靜態變量共享。必須給它指定一個名稱,才能使兩個應用程序訪問同一個Mutex對象。
Mutex類的常用方法及說明如表1所示。
表1  Mutex類的常用方法及說明

方法

說明

Close

在派生類中被重寫時,釋放由當前WaitHandle持有的所有資源

OpenExisting

打開現有的已命名互斥體

ReleaseMutex

釋放Mutex一次

SignalAndWait

原子操作的形式,向一個WaitHandle發出信號並等待另一個

WaitAll

等待指定數組中的所有元素都收到信號

WaitAny

等待指定數組中的任一元素收到信號

WaitOne

當在派生類中重寫時,阻止當前線程,直到當前的WaitHandle收到信號

使用Mutex類實現線程同步很簡單,首先實例化一個Mutex類對象,它的構造函數中比較常用的有public Mutex(bool initallyOwned),其中,參數initallyOwned指定了創建該對象的線程是否希望立即獲得其所有權,當在一個資源得到保護的類中創建Mutex類對象時,常將該參數設置爲false;然後在需要單線程訪問的地方調用其等待方法,等待方法請求Mutex對象的所有權,這時,如果該所有權被另一個線程所擁有,則阻塞請求線程,並將其放入等待隊列中,請求線程將保持阻塞,直到Mutex對象收到了其所有者線程發出將其釋放的信號爲止。所有者線程在終止時釋放Mutex對象,或者調用ReleaseMutex方法來釋放Mutex對象。
 說明:儘管Mutex類可以用於進程內的線程同步,但是使用Monitor類通常更爲可取,因爲Monitor監視器是專門爲.NET Framework而設計的,因而它可以更好地利用資源。相比之下,Mutex類是Win32構造的包裝。儘管Mutex類比監視器更爲強大,但是相對於Monitor類,它所需要的互操作轉換更消耗計算資源。
例如創建一個控制檯應用程序,其中自定義了一個LockThread方法,該方法中首先使用Mutex類對象的WaitOne方法阻止當前線程,然後再調用Mutex類對象的ReleaseMutex方法釋放Mutex對象,即釋放當前線程,最後在Main方法中通過Program的類對象調用LockThread自定義方法。代碼如下。

static void Main(string[] args)
{
    Program myProgram = new Program();//實例化類對象
    myProgram.LockThread();//調用鎖定線程方法
}
void LockThread()
{
    Mutex myMutex=new Mutex(false); //實例化Mutex類對象
    myMutex.WaitOne();//阻止當前線程
    Console.WriteLine("鎖定線程以實現線程同步");
    myMutex.ReleaseMutex();//釋放Mutex對象
}

標籤: 線程
發佈了15 篇原創文章 · 獲贊 14 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章