1. 程式人生 > >C#多執行緒中鎖的使用

C#多執行緒中鎖的使用

最近的專案中涉及到實時資料的處理,經常會使用多執行緒訪問共享資源。如果處理不當,資源未能正確在各個執行緒中同步的話,計算結果將會出現錯誤。

關於資源同步最常用的技術就是加鎖。這裡提到是一個比較簡單的鎖 -- lock。 lock是對monitor中的兩個函式enter和exit的封裝。

當時專案的模式是這樣的:有一個類中有個共享的資源(List),這個類會開闢兩個執行緒分別對它進行讀和寫操作,而且這個類會有多個例項,每個例項都會在一個執行緒中。

其工作模式如下圖所示:


這裡的寫執行緒將會動態改變list裡面的值已經值對應的index,而讀的執行緒會先用一個匹配的函式找出需要的值對應的index,然後根據index取出其中的值加以計算。

在這種模式中,如果讀執行緒去訪問list資料時,寫執行緒對list進行了修改,就會導致結果出錯。因此我們需要引入鎖的機制。那麼問題來了,我們是要在讀的執行緒中加鎖,還是在寫的執行緒中加鎖。而且加鎖的話,是採用什麼方式?

以下有幾種方式,將會一一分析其對錯。

1. 使用物件本身作為鎖物件,對寫執行緒進行加鎖

使用這種方式,在寫執行緒中,list將會被鎖定,執行完畢之後釋放鎖。然而這種模式還是會得出錯誤結果,假設在讀執行緒中先對list進行了匹配並取出了需要的index,這時寫執行緒獲取了鎖,並對list進行了修改,之後釋放list,讀執行緒根據index查詢list對應的值,然而此時list已被更改。

2. 使用物件本身作為鎖物件,對讀執行緒進行加鎖

使用這種方式,在讀執行緒中,list將會被鎖定,所以讀的過程中list都將保持不變。

除了使用物件本身作為鎖物件之外,還可以使用其他的引用型別,比如System.Object,此時執行緒不會訪問該型別的任何屬性和方法,改物件的作用僅僅是協調各個執行緒。

3. 使用System.Object作為鎖物件

根據上面的分析,我們將只對讀執行緒進行加鎖。但是這時又有兩種情況:object物件可以定義成例項物件和靜態物件。目前的這種模式適用於把鎖加在例項物件的object上。因為使用object作為鎖物件時,操作流程時佔有A,操作B,釋放A。若將object定義為靜態物件,此時對object進行加鎖,將協調的是類的每個例項所產生的物件,對應的list也必須是靜態的。而這裡我們需要協調的是某一個例項中的兩個執行緒,因此需要將object定義為例項物件。

綜上所述,有兩種方式可以實現以上模式的執行緒同步,一種是使用物件本身作為鎖物件,對讀執行緒進行加鎖,另一種是使用例項物件的System.Object作為鎖物件,對讀執行緒程序加鎖。