C 執行緒鎖使用全功略

2022-02-18 13:56:08 字數 3383 閱讀 9265

前兩篇簡單介紹了執行緒同步lock,monitor,同步事件eventwaithandler,互斥體mutex的基本用法,在此基礎上,我們對 它們用法進行比較,並給出什麼時候需要鎖什麼時候不需要的幾點建議。最後,介紹幾個fcl中線程安全的類,集合類的鎖定方式等,做為對執行緒同步系列的完善 和補充。

lock和monitor是.net用乙個特殊結構實現的,monitor物件是完全託管的、完全可移植的,並且在作業系統資源要求方 面可能更為有效,同步速度較快,但不能跨程序同步。lock(monitor.enter和monitor.exit方法的封裝),主要作用是鎖定臨界區,使臨 界區**只能被獲得鎖的執行緒執行。monitor.wait和monitor.pulse用於執行緒同步,類似訊號操作,個人感覺使用比較複雜,容易造成死 鎖。

互斥體mutex和事件物件eventwaithandler屬於核心物件,利用核心物件進行執行緒同步,執行緒必須要在使用者模式和核心模 式間切換,所以一般效率很低,但利用互斥物件和事件物件這樣的核心物件,可以在多個程序中的各個執行緒間進行同步。

互斥體mutex類似於乙個接力棒,拿到接力棒的執行緒才可以開始跑,當然接力棒一次只屬於乙個執行緒(thread affinity),如果這個執行緒不釋放接力棒(mutex.releasemutex),那麼沒辦法,其他所有需要接力棒執行的執行緒都知道能等著看熱 鬧。

eventwaithandle 類允許執行緒通過發訊號互相通訊。 通常,乙個或多個執行緒在 eventwaithandle 上阻止,直到乙個未阻止的執行緒呼叫 set 方法,以釋放乙個或多個被阻止的執行緒。

首先要理解鎖定是解決競爭條件的,也就是多個執行緒同時訪問某個資源,造成意想不到的結果。比如,最簡單的情況是,乙個計數器,兩個執行緒 同時加一,後果就是損失了乙個計數,但相當頻繁的鎖定又可能帶來效能上的消耗,還有最可怕的情況死鎖。那麼什麼情況下我們需要使用鎖,什麼情況下不需要 呢?

1)只有共享資源才需要鎖定

只有可以被多執行緒訪問的共享資源才需要考慮鎖定,比如靜態變數,再比如某些快取中的值,而屬於執行緒內部的變數不需要鎖定。 

2)多使用lock,少用mutex

如果你一定要使用鎖定,請盡量不要使用核心模組的鎖定機制,比如.net的mutex,semaphore,autoresetevent和 manuresetevent,使用這樣的機制涉及到了系統在使用者模式和核心模式間的切換,效能差很多,但是他們的優點是可以跨程序同步執行緒,所以應該清 楚的了解到他們的不同和適用範圍。

4)把鎖定交給資料庫

數 據庫除了儲存資料之外,還有乙個重要的用途就是同步,資料庫本身用了一套複雜的機制來保證資料的可靠和一致性,這就為我們節省了很多的精力。保證了資料來源 頭上的同步,我們多數的精力就可以集中在快取等其他一些資源的同步訪問上了。通常,只有涉及到多個執行緒修改資料庫中同一條記錄時,我們才考慮加鎖。 

5)業務邏輯對事務和執行緒安全的要求

這 條是最根本的東西,開發完全執行緒安全的程式是件很費時費力的事情,在電子商務等涉及金融系統的案例中,許多邏輯都必須嚴格的執行緒安全,所以我們不得不犧牲 一些效能,和很多的開發時間來做這方面的工作。而一般的應用中,許多情況下雖然程式有競爭的危險,我們還是可以不使用鎖定,比如有的時候計數器少一多一, 對結果無傷大雅的情況下,我們就可以不用去管它。

interlocked 類提供了同步對多個執行緒共享的變數的訪問的方法。如果該變數位於共享記憶體中,則不同程序的執行緒就可以使用該機制。互鎖操作是原子的,即整個操作是不能由相 同變數上的另乙個互鎖操作所中斷的單元。這在搶先多執行緒作業系統中是很重要的,在這樣的作業系統中,執行緒可以在從某個記憶體位址載入值之後但是在有機會更改 和儲存該值之前被掛起。

我們來看乙個interlock.increment()的例子,該方法以原子的形式遞增指定變數並儲存結果,示例如下:

class

interlockedtest

}public

static

void

main(

string

args)

}輸出結果200000000,如果interlockedtest.add()方法中用注釋掉的語句代替interlocked.increment() 方法,結果將不可預知,每次執行結果不同。interlockedtest.add()方法保證了加1操作的原子性,功能上相當於自動給加操作使用了 lock鎖。同時我們也注意到interlockedtest.add()用時比直接用+號加1要耗時的多,所以說加鎖資源損耗還是很明顯的。

另外interlockedtest類還有幾個常用方法,具體用法可以參考msdn上的介紹。

.net在一些集合類,比如queue、arraylist、hashtable和stack,已經提供了乙個供lock使用的物件syncroot。用 reflector檢視了syncroot屬性(stack.synchroot略有不同)的原始碼如下:

public

virtual

object

syncroot

return

this

._syncroot;}}

這裡要特別注意的是msdn提到:從頭到尾對乙個集合進行列舉本質上並不是乙個執行緒安全的過程。即使乙個集合已進行同步,其他執行緒仍可以修改該集合,這將 導致列舉數引發異常。若要在列舉過程中保證執行緒安全,可以在整個列舉過程中鎖定集合,或者捕捉由於其他執行緒進行的更改而引發的異常。應該使用下面的**:

queue使用lock示例

queue q 

=new

queue();

lock

(q.syncroot)

}//在多執行緒環境中只要我們用下面的 方式例項化hashtable就可以了

hashtable ht 

=hashtable.synchronized(

newhashtable());

//以下**是.net framework class library實現,增加對 synchronized的認識

[hostprotection(securityaction.linkdemand, synchronization

=true

)]public

static

hashtable synchronized(hashtable table)

return

newsynchashtable(table);}//

synchashtable的幾個常用方法,我們可以看到內部實現都加了lock關鍵字 保證執行緒安全

public

override

void

add(

object

key, 

object

value)

}public

override

void

clear()

}public

override

void

remove(

object

key)

}

C 執行緒鎖使用全功略

前兩篇簡單介紹了執行緒同步lock,monitor,同步事件eventwaithandler,互斥體mutex的基本用法,在此基礎上,我們對 它們用法進行比較,並給出什麼時候需要鎖什麼時候不需要的幾點建議。最後,介紹幾個fcl中線程安全的類,集合類的鎖定方式等,做為對執行緒同步系列的完善 和補充。l...

c 執行緒鎖,mutex lock使用

為什麼要用執行緒鎖呢.比如當乙個執行緒再修改乙個變數,另乙個執行緒在讀取那個變數,那麼讀到的值可能是乙個無法 的值,因為有可能他在讀的時候另乙個執行緒正寫到一半.由於執行緒導致的bug會比較難查詢,因此寫乙個靠譜的執行緒鎖非常重要.總結一下mutex lock相關函式 int pthread mut...

C 操作Word完全功略

前提 匯入com庫 microsoft word 11.0 object library.引用裡面就增加了 建立新word object newtrue odoc oword.documents.add refomissing,refomissing,refomissing,refomissing ...