簡體   English   中英

C ++ 11多線程鎖和原子基元

[英]C++11 multithreading locks and atomic primitives

(一個問題導致兩個問題)

在C ++ 11中,鎖是原子的,可以確定兩個線程不能同時獲取鎖嗎?

如果以上答案為“是”,那么雙重檢查鎖定的目的是什么?

最后,為什么只使用原子原語(顯然,我們知道是atomic )並將其設置為1或0(取決於是否已獲取鎖)時需要std :: lock()?

雙重檢查鎖定背后的困惑想法是在檢查條件時獲取鎖定,而僅在不太可能的條件下獲取鎖定。 但是,要使其正常工作,仍然需要進行大量同步,這很容易出錯。 由於雙重檢查鎖定的主要用途是初始化單例(這本身不是一個好主意)和跨線程共享的常量對象,因此C ++ 11實際上實現了函數本地static對象的線程安全初始化。 這應該消除了大多數情況下人們試圖獲得雙重檢查鎖定權的情況。

除此之外:互斥鎖的要點是,最多一個線程可以獲取一個互斥鎖,即,保證沒有兩個線程可以獲取相同的鎖。 關於使用原子變量來表示類似於鎖的內容,您需要了解,鎖定/解鎖互斥鎖會增加其他同步,而不是通過更改原子值來完成:僅知道一個線程是不夠的修改共享狀態,還必須向系統發出更改信號。 同樣,當無法獲取鎖時,普通鎖可能會中止線程執行(盡管我不認為實現是必需的,即,我認為它可以使用自旋鎖進行繁忙的等待)。

鎖是100%原子的,除非您嘗試做一些聰明的事情,例如在有人獲得它時銷毀它。

鎖定需要花費時間。 如果您只需要檢查鎖的一小部分時間,則雙重檢查鎖可以避免鎖成本,並且僅在無法證明跳過鎖的安全性時才需要鎖。

您不能簡單地用原子原語替換鎖,因為您可以等待鎖。 如果等待鎖,則操作系統將停止運行該線程,並在其他地方花費其CPU功能。 如果您在原子原語上循環運行,則會使CPU忙碌而不做有用的事情。

話雖這么說,但有一種以這種方式構建的鎖結構,稱為自旋鎖,如果您可以預期它只能鎖幾個周期,那么這種鎖結構將非常快。

另外,您不能將condition_variables與原子變量一起使用,需要真正的鎖

是的,C ++ 11鎖是原子鎖:一次只能有一個線程在單個std::mutex上擁有std::mutex 這就是互斥的全部目的:提供互斥。

雙重檢查鎖定的目的是避免不需要時獲得鎖定的開銷:特別是避免當多個線程同時運行同一位代碼並且不再需要互斥時的互斥和隨后的序列化。

這通常用於某種形式的一次性初始化,並且仍然需要同步。 這種同步可以用原子完成,但是很難做到。 我確實在我寫的博客文章中提到了如何做到這一點( 惰性初始化和原子雙重檢查鎖定 ),但通常最好還是使用局部static對象或std::call_once

在某些情況下,可以用原子標志替換互斥對象​​,但是很難實現正確的同步:通常,該標志僅表示可以訪問其他數據,並且您需要確保正確同步這些數據。 除非通過分析表明互斥鎖是瓶頸,否則您最好堅持使用互斥鎖,而不是嘗試使用原子同步自己的同步。

最后, std::lock()要點是一次鎖定多個互斥鎖而不會出現死鎖。 通常,您必須一次鎖定一個互斥鎖。 但是,如果線程1鎖定互斥鎖A,然后鎖定互斥鎖B,線程2鎖定互斥鎖B,然后鎖定互斥鎖A,則可能會出現死鎖。 std::lock()通過等待直到線程可以獲取兩個互斥量來避免這種情況,而又不會阻止其他線程同時鎖定它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM