2017-10-21 79 views
1

我想創建一個物理引擎(只是爲了好玩),我希望它是多線程的。
我瞭解互斥體的基本知識(只有一個線程可以修改它一次監視的資源,它們應該是在一個級別而不是線程級別等)。我寧願不使用原子的成員變量(所以如果我正在對它們進行復雜的操作,它們不會在執行過程中正確地更改),或者只是簡單地複製這些變量(希望能夠降低內存佔用量)。某人如何在一個線程中鎖定多個對象?

按照這個概念,(簡化)載體類可能是這樣的:

class vector 
{ 
    float x_, y_; 
    std::mutex guard_; 
}; 

如果我想使用它,又該如何鎖定?

void foo(vector v1, vector v2) 
{ 
    std::lock_guard<std::mutex>(v1.guard_); 
    std::lock_guard<std::mutex>(v2.guard_); 
    // Do stuff with v1 and v2... 
} 

是這樣的嗎?這實際上會保護這兩個對象嗎?

TL; DR當多個對象被同一個線程操作時,應該如何鎖定互斥鎖? (不使用原子或製作副本)

+1

看起來不錯,假設所有訪問對象的代碼使用前鎖定他們。警惕[死鎖](https://en.wikipedia.org/wiki/Deadlock),這在鎖定多個對象時經常發生。 – hnefatl

+0

請注意,互斥量可能與兩個「浮點數」一樣大,所以相對於基於複製的某些假設解決方案,您可能不會節省內存。 –

+1

互斥量保護代碼路徑不是對象(變量) - 這是保護一個或多個代碼路徑的尺寸效應。 –

回答

3

你寫的很好,有一個臭名昭着的警告:如果兩個線程嘗試採取相同的兩個鎖,但以相反的順序,將會死鎖。這由經典的dining philosophers問題來說明。

標準的解決方案是在鎖上施加一些固定的,任意的順序,並在所有情況下首先採取「較低」的順序。 user3290797的答案提供了正確的庫設施來用來做到這一點。

5
std::lock_guard<std::mutex>(v1.guard_); 
std::lock_guard<std::mutex>(v2.guard_); 

這種方式有死鎖的風險,如果另一個線程試圖鎖定相同的兩個互斥體以不同的順序。

爲了既避免死鎖,確保異常安全,你需要一次先鎖定兩個互斥體,然後通過已鎖定互斥來lock_guard的採用構造函數:

std::lock(v1.guard_, v2.guard_); 
std::lock_guard<std::mutex> guard1(v1.guard_, std::adopt_lock); 
std::lock_guard<std::mutex> guard2(v2.guard_, std::adopt_lock); 

在C++ 17你可以使用std::scoped_lock,一類類似於std::lock_guard,但能夠擁有多個互斥的:

std::scoped_lock guard{v1.guard_, v2.guard_}; 
相關問題