2017-10-21 81 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_}; 
相关问题