我有一个对象,其所有功能都应按顺序执行。 我知道可以使用像
这样的互斥体来做到这一点#include <mutex>
class myClass {
private:
std::mutex mtx;
public:
void exampleMethod();
};
void myClass::exampleMethod() {
std::unique_lock<std::mutex> lck (mtx); // lock mutex until end of scope
// do some stuff
}
但是使用这种技术在exampleMethod中调用其他互斥锁定方法后会发生死锁。
所以我正在寻找更好的解决方案。 默认的std :: atomic访问是顺序一致的,所以它不可能同时读取对这个对象的写入,但是现在当我访问我的对象并调用一个方法时,整个函数调用也是原子的或更像是
object* obj = atomicObj.load(); // read atomic
obj.doSomething(); // call method from non atomic object;
如果是,是否有比使用互斥锁锁定大多数函数更好的方法?
答案 0 :(得分:3)
停止并考虑何时需要锁定互斥锁。如果你有许多其他函数调用的辅助函数,它可能不应该尝试锁定互斥锁,因为调用者已经有了。
如果在某些上下文中它没有被另一个成员函数调用,那么需要锁定,提供实际执行此操作的包装函数。拥有2个版本的成员函数(公共foo()
和私有fooNoLock()
)并不罕见,其中:
public:
void foo() {
std::lock_guard<std::mutex> l(mtx);
fooNoLock();
}
private:
void fooNoLock() {
// do stuff that operates on some shared resource...
}
根据我的经验,递归互斥体是一个code smell,表明作者没有真正了解函数的使用方式 - 不是总是错误,但当我看到一个我怀疑。
对于原子操作,它们实际上只能用于小算术运算,比如递增整数或交换2个指针。这些操作不是自动原子操作,但是当您使用原子操作时,这些操作可以用于它们。你当然不能对单个原子对象的2个单独操作有任何合理的期望。在操作之间可能发生任何事情。
答案 1 :(得分:0)
您可以使用std::recursive_mutex代替。这将允许已拥有互斥锁的线程重新获取它而不会阻塞。但是,如果另一个线程试图获取锁定,它将阻止。
答案 2 :(得分:0)
正如@BoBTFish正确指出的那样,最好将你的类的公共接口分开,哪些成员函数获得非递归锁定,然后调用不需要的私有方法。然后,您的代码必须假定在运行私有方法时始终保持锁定。
为了安全起见,您可以为需要锁定的每个方法添加对std::unique_lock<std::mutex>
的引用。
因此,即使您碰巧从另一个私有方法调用一个私有方法,您也需要确保在执行之前锁定互斥锁:
class myClass
{
std::mutex mtx;
//
void i_exampleMethod(std::unique_lock<std::mutex> &)
{
// execute method
}
public:
void exampleMethod()
{
std::unique_lock<std::mutex> lock(mtx);
i_exampleMethod(lock);
}
};