多執行緒同步問題

2021-05-04 01:25:14 字數 3751 閱讀 4562

在應用程式中使用多個執行緒的乙個好處是每個執行緒都可以非同步執行。對於 windows 應用程式,耗時的任務可以在後台執行,而使應用程式視窗和控制項保持響應。對於伺服器應用程式,多執行緒處理提供了用不同執行緒處理每個傳入請求的能力。否則,在完全滿足前乙個請求之前,將無法處理每個新請求。

然而,執行緒的非同步特性意味著必須協調對資源(如檔案控制代碼、網路連線和記憶體)的訪問。否則,兩個或更多的執行緒可能在同一時間訪問相同的資源,而每個執行緒都不知道其他執行緒的操作。結果將產生不可預知的資料損壞。

對於整數資料型別的簡單操作,可以用 interlocked

類的成員來實現執行緒同步。對於其他所有資料型別和非執行緒安全的資源,只有使用本主題中的結構才能安全地執行多執行緒處理(摘自msdn)。

1:lock 關鍵字

lock關鍵字可以用來確保**塊完成執行,而不會被其他執行緒中斷。這是通過在**塊執行期間為給定物件獲取互斥鎖來實現的。

lock語句以關鍵字lock開頭,它有乙個作為引數的物件,在該引數的後面還有乙個一次只能由乙個執行緒執行的**塊。例如:

c#

public

void function()

}

提供給lock關鍵字的引數必須為基於引用型別的物件,該物件用來定義鎖的範圍。在上例中,鎖的範圍限定為此函式,因為函式外不存在任何對該物件的引用。嚴格地說,提供給lock的物件只是用來唯一地標識由多個執行緒共享的資源,所以它可以是任意類例項。然而,實際上,此物件通常表示需要進行執行緒同步的資源。例如,如果乙個容器物件將被多個執行緒使用,則可以將該容器傳遞給 lock,而 lock 後面的同步**塊將訪問該容器。只要其他執行緒在訪問該容器前先鎖定該容器,則對該物件的訪問將是安全同步的。

通常,最好避免鎖定public型別或鎖定不受應用程式控制的物件例項。例如,如果該例項可以被公開訪問,則 lock(this) 可能會有問題,因為不受控制的**也可能會鎖定該物件。這可能導致死鎖,即兩個或更多個執行緒等待釋放同一物件。出於同樣的原因,鎖定公共資料型別(相比於物件)也可能導致問題。鎖定字串尤其危險,因為字串被公共語言執行庫 (clr)「暫留」。這意味著整個程式中任何給定字串都只有乙個例項,就是這同乙個物件表示了所有執行的應用程式域的所有執行緒中的該文字。因此,只要在應用程式程序中的任何位置處具有相同內容的字串上放置了鎖,就將鎖定應用程式中該字串的所有例項。因此,最好鎖定不會被暫留的私有或受保護成員。某些類提供專門用於鎖定的成員。例如,array

型別提供 syncroot

。許多集合型別也提供 syncroot。

2:監視器

lock關鍵字類似,監視器防止多個執行緒同時執行**塊。enter

方法允許乙個且僅乙個執行緒繼續執行後面的語句;其他所有執行緒都將被阻止,直到執行語句的執行緒呼叫 exit

。這與使用lock關鍵字一樣。事實上,lock關鍵字就是用 monitor

類來實現的。例如:

c#

複製**

lock(x)

這等效於:

c#

複製**

system.object obj = (system.object)x;

system.threading.monitor.enter(obj);

tryfinally

使用lock關鍵字通常比直接使用monitor類更可取,一方面是因為lock更簡潔,另一方面是因為lock確保了即使受保護的**引發異常,也可以釋放基礎監視器。這是通過finally關鍵字來實現的,無論是否引發異常它都執行關聯的**塊。

有關監視器的更多資訊,請參見監視器同步技術示例

。同步事件和等待控制代碼

使用鎖或監視器對於防止同時執行區分執行緒的**塊很有用,但是這些構造不允許乙個執行緒向另乙個執行緒傳達事件。這需要「同步事件」,它是有兩個狀態(終止和非終止)的物件,可以用來啟用和掛起執行緒。讓執行緒等待非終止的同步事件可以將執行緒掛起,將事件狀態更改為終止可以將執行緒啟用。如果執行緒試圖等待已經終止的事件,則執行緒將繼續執行,而不會延遲。

同步事件有兩種:autoresetevent

和 manualresetevent

。它們之間唯一的不同在於,無論何時,只要autoresetevent啟用執行緒,它的狀態將自動從終止變為非終止。相反,manualresetevent允許它的終止狀態啟用任意多個執行緒,只有當它的 reset

方法被呼叫時才還原到非終止狀態。

可以通過呼叫一種等待方法,如 waitone

、waitany

或 waitall

,讓執行緒等待事件。system.threading.waithandle.waitone

使執行緒一直等待,直到單個事件變為終止狀態;system.threading.waithandle.waitany

阻止執行緒,直到乙個或多個指示的事件變為終止狀態;system.threading.waithandle.waitall

阻止執行緒,直到所有指示的事件都變為終止狀態。當呼叫事件的 set

方法時,事件將變為終止狀態。

在下面的示例中,建立了乙個執行緒,並由 main 函式啟動該執行緒。新執行緒使用waitone方法等待乙個事件。在該事件被執行 main 函式的主線程終止之前,該執行緒一直處於掛起狀態。一旦該事件終止,輔助線程將返回。在本示例中,因為事件只用於乙個執行緒的啟用,所以使用autoreseteventmanualresetevent類都可以。

c#

複製**

using system;

using system.threading;

class threadingexample

static

void main()

}

3:mutex 物件

mutex 與監視器類似;它防止多個執行緒在某一時間同時執行某個**塊。事實上,名稱「mutex」是術語「互相排斥 (mutually exclusive)」的簡寫形式。然而與監視器不同的是,mutex 可以用來使跨程序的執行緒同步。mutex 由 mutex

類表示。

當用於程序間同步時,mutex 稱為「命名 mutex」,因為它將用於另乙個應用程式,因此它不能通過全域性變數或靜態變數共享。必須給它指定乙個名稱,才能使兩個應用程式訪問同乙個 mutex 物件。

儘管 mutex 可以用於程序內的執行緒同步,但是使用monitor通常更為可取,因為監視器是專門為 .net framework 而設計的,因而它可以更好地利用資源。相比之下,mutex類是 win32 構造的包裝。儘管 mutex 比監視器更為強大,但是相對於monitor類,它所需要的互操作轉換更消耗計算資源。有關 mutex 的用法示例,請參見 mutex

。c#

private mutex mutf = new mutex();

private void readf()  

多執行緒同步問題

有四個執行緒1,2,3,4,執行緒1的功能就是輸出1,執行緒2的功能就是輸出2,以此類推.現在有四個檔案.abcd.初始都為空.現要讓四個檔案呈如下格式 a 1 2 3 4 1 2.b 2 3 4 1 2 3.c 3 4 1 2 3 4.d 4 1 2 3 4 1.設計程式.include sys ...

多執行緒 同步問題

我們先看個錯誤示例。功能 宣告乙個數字並賦值10000.然後讓1w個執行緒去減少1,1w個執行緒去增加1。理論上說,加一萬減一萬,最後數字的結果並不會改變。class errordemo 釋放object的方法 synchronized 塊結束或者異常丟擲。使用synchronized後的 publ...

多執行緒 同步問題

我們先看個錯誤示例。功能 宣告乙個數字並賦值10000.然後讓1w個執行緒去減少1,1w個執行緒去增加1。理論上說,加一萬減一萬,最後數字的結果並不會改變。class errordemo t1.start reduce i t1 建立1w個增加執行緒 for int i 0 i t2.start a...