多執行緒請勿使用雙重檢驗鎖保護資料初始化

2021-09-25 20:08:44 字數 1623 閱讀 7456

在前些年,雙重檢驗鎖據說被廣泛的運用。其形式如下:

#include #include using namespace std;

class someobject

};class badatempt

} somedetail->hello();

}};

首先要明確的是為什麼要用雙重檢驗鎖:為了保護資料的初始化過程,光這麼說你或許還不能了解其真正內涵,那咱們首先這麼想,先不管多執行緒,在單程序下建立乙個類的例項,badatempt a;就行了,我們要明確的是這個例項中有個型別為someobject的成員變數沒有初始化。為什麼要用指標,就是為了延遲初始化,因為或許你一輩子都不會在這個例項中使用這個成員變數,那又何必初始化它占用多餘的空間。使用指標初始化,就需要考慮多執行緒所帶來的問題了。

多執行緒是併發執行的,在同一時間對同乙個例項,可能有多個執行緒同時初始化這個someobject型別的成員變數,因為在它們看來,這個變數都還未初始化。

void init()

somedetail->hello();

}

這當然就會導致異常了,那你說好,那我加個鎖不就行了。

void init()

somedetail->hello();

}//釋放鎖,允許下個阻塞的執行緒上鎖繼續。

這確實行,但你不覺得慢麼,會相當慢,因為這種情況下如果有100個執行緒同時初始化,第乙個執行緒上鎖,其他99個就得等。第乙個結束了,第二個上鎖,其他98個等……這叫做執行緒的序列化,多執行緒硬生生變成了好像單程序一樣順序執行。

所以應運而生了雙重檢驗鎖

void init()

}//ok,釋放鎖,其他所有執行緒都不會再序列化的被阻塞。

somedetail->hello();

}

看我的注釋,你會感覺這種方式,哎喲不錯哦。但它其實內涵乙個惡劣的race condition。你要注意乙個關鍵點,這個關鍵點也是避免序列化的關鍵:第乙個判斷沒有上鎖,你再注意第二個關鍵點:判斷如果是false,接下來的語句是somedetail指標呼叫hello函式。仔細想想,在多執行緒併發情況下,如果乙個執行緒已經進行到了給指標賦予指向的物件的語句並且正在賦值還沒賦值完(正在執行第8行),其他執行緒是可以進行第乙個判斷的因為第乙個判斷沒有上鎖,但是它判斷的是false!它以為指標已經指向了正確的物件,然後它就繼續呼叫hello(),自然而然,未定義的情況就出現了。這確實是個不容易發現但很致命的race condition。

你說那怎麼辦?初始化後不直接呼叫指標?總不是個長久之計(甚至連個計都算不上)。

class a

void somedetailhello()

};

****** and easy.

多執行緒中鎖的使用

執行緒之間的鎖有 互斥鎖 條件鎖 自旋鎖 讀寫鎖 遞迴鎖。一般而言,鎖的功能越強大,效能就會越低。1 互斥鎖 互斥鎖用於控制多個執行緒對他們之間共享資源互斥訪問的乙個訊號量。也就是說是為了避免多個執行緒在某一時刻同時操作乙個共享資源。例如執行緒池中的有多個空閒執行緒和乙個任務佇列。任何是乙個執行緒都...

多執行緒中鎖的使用

本文 主要介紹下c 的兩種鎖 互斥鎖 lock 和讀寫鎖 readwritelock 互斥鎖 lock lock 關鍵字將語句塊標記為臨界區,方法是獲取給定物件的互斥鎖,執行語句,然後釋放該鎖。當任何乙個執行緒獲取到鎖後,其他執行緒如果需要使用該臨界區內 則必須等待前乙個執行緒使用完畢後釋放鎖。示例...

C 多執行緒中鎖的使用

最近的專案中涉及到實時資料的處理,經常會使用多執行緒訪問共享資源。如果處理不當,資源未能正確在各個執行緒中同步的話,計算結果將會出現錯誤。關於資源同步最常用的技術就是加鎖。這裡提到是乙個比較簡單的鎖 lock。lock是對monitor中的兩個函式enter和exit的封裝。當時專案的模式是這樣的 ...