c++ Boost线程.如何使用递归尝试锁?死锁发生在并发代码中
C++ Boost threads. How to use recursive try lock? Deadlock happens in concurrent code
我正在使用boost在c++中开发一个线程安全的惰性对象模式。然而,当我这样做时,如果我的应用程序有多个线程,我就会在LazyObject
的calculate()
方法中进入死锁状态。
这在某种程度上与boost::recursive_mutex::scoped_try_lock
有关,因为一旦我用常规互斥锁屏蔽了代码,并让其他线程等待这个互斥锁,一切都很好。然而,仅仅阻塞其他线程的缺点是,它们实际上都需要通过耗时的performCalculations()
,因为一个线程经常将calculated_
标志更改为false
。还要注意,performCalculations()
是纯虚拟的,派生实例将递归地调用LazyObject::calculate()
。我想用互斥锁来保护这个无限递归
你能看出我错在哪里吗?
我的LazyObject
有以下属性:
// protects resource frozen_ from simultaneous freeze, unfreeze calls
mutable boost::mutex frozenMutex_;
// protects resource calculated_ from simultaneous update, calculate calls
mutable boost::mutex calculatedMutex_;
// protects that only one thread can simultaneously call calculate
mutable boost::recursive_try_mutex waitMutex_;
// protects that only once can performCalculations be called (from same thread)
mutable boost::mutex blockingMutex_;
// mutex and semaphore for sleeping threads until calculate is ready
mutable boost::mutex condMutex_;
mutable boost::condition_variable condVariable_;
inline void LazyObject::calculate() const {
boost::recursive_mutex::scoped_try_lock lock(waitMutex_);
if (lock) {
//recursive lock lets same thread pass, puts others on wait
if (!calculated_ && !frozen_ && blockingMutex_.try_lock()) {
// blockingMutex ensures that only once same thread
// can call performCalculations
try {
performCalculations();
calculated_ = true;
blockingMutex_.unlock();
condVariable_.notify_all();
} catch (...) {
calculated_ = false;
blockingMutex_.unlock();
condVariable_.notify_all();
throw;
}
}
} else {
// start a non blocking wait until calculation is ready
boost::mutex::scoped_lock lock(condMutex_);
condVariable_.wait(lock);
}
}
您提供的一个函数看起来不错。
然而,我强烈怀疑你有锁排序问题。在一个类中有5个互斥体。您需要保证这些互斥锁总是以相同的顺序锁定。否则就会死锁。
看起来你有一个非常复杂的锁顺序:
- 5个不同的互斥体
- 1是递归的
也许您可以提供您想要实现的目标的描述。您没有提供完整的代码,所以只能猜测。
例如,如果一个线程刚刚将calculated_
设置为true
,执行了condVariable_.notifyAll()
,在解锁waitMutex_
之前被抢占,然后另一个线程阻塞在condVariable_.wait(lock)
,然后没有人来唤醒它,你可以死锁。
我看到你在评论中写了"互斥锁和信号量",注意条件变量没有内存,它不像信号量或Windows事件对象。
给出一个更好的问题描述,我真的不认为上面的代码是可利用的:)
你是对的,上面的函数并没有给出一个清晰的全貌。基本上下面是所有相互作用的函数,它们争夺*this
的资源我能够将互斥体的数量减少到只使用3个。但我认为这个问题不能实际解决少数量的互斥锁。前提条件是更新方法必须尽可能便宜。
我还有一个关于异常抛出的问题。如您所见,执行performcomputation的计算线程可能会抛出异常。如果有一些线程等待信号继续,它们就不能继续,因为甚至发生了任何异常。是否有可能使用boost以某种方式让唤醒线程抛出与信令线程抛出相同的异常?如果是,你能提供明确的代码如何工作的想法?
我的类需要下列属性。
// state variables indicating is calculation necessary
mutable bool calculated_, frozen_;
// flag that tells waking threads to throw exceptions if
// LazyObject::performCalculations() threw any exceptions
mutable bool failed_;
// flag avoiding infinite recursion on single thread not recursively
// calling LazyObject::performCalculations() through recursive calls
// to LazyObject::calculate()
mutable bool calculating_;
// protects resources from simultaneous read & writes
mutable boost::mutex readWriteMutex_;
// protects that only one thread can simultaneously call calculate
//mutable boost::mutex waitMutex_;
mutable boost::recursive_try_mutex waitMutex_;
// mutex and semaphore for sleeping threads until calculate is ready
mutable boost::mutex condMutex_;
mutable boost::condition_variable condVariable_;
inline void LazyObject::performCalculations() {
// let derived classes specialize own implementation
}
inline void LazyObject::update() {
// observers don't expect notifications from frozen objects
// LazyObject forwards notifications only once until it has been
// recalculated
readWriteMutex_.lock();
calculated_ = false;
readWriteMutex_.unlock();
if (!frozen_) {
notifyObservers();
}
}
inline void LazyObject::recalculate() {
readWriteMutex_.lock();
bool wasFrozen = frozen_;
calculated_ = frozen_ = false;
try {
readWriteMutex_.unlock();
calculate();
} catch (...) {
readWriteMutex_.lock();
frozen_ = wasFrozen;
readWriteMutex_.unlock();
notifyObservers();
throw;
}
readWriteMutex_.lock();
frozen_ = wasFrozen;
readWriteMutex_.unlock();
notifyObservers();
}
inline void LazyObject::freeze() {
readWriteMutex_.lock();
frozen_ = true;
readWriteMutex_.unlock();
}
inline void LazyObject::unfreeze() {
readWriteMutex_.lock();
frozen_ = false;
readWriteMutex_.unlock();
// send notification, just in case we lost any
notifyObservers();
}
inline void LazyObject::calculate() const {
//boost::recursive_mutex::scoped_try_lock lock(waitMutex_);
readWriteMutex_.lock();
// see a snapshot of object's status
if (!calculated_ && !frozen_) {
if (waitMutex_.try_lock()) {
//recursive lock lets same thread pass, puts others on wait
if (calculating_) {
readWriteMutex_.unlock();
waitMutex_.unlock();
return;
} else {
calculating_ = true;
}
readWriteMutex_.unlock();
try {
performCalculations();
readWriteMutex_.lock();
calculating_ = false;
failed_ = false;
calculated_ = true;
readWriteMutex_.unlock();
waitMutex_.unlock();
condVariable_.notify_all();
return;
} catch (...) {
readWriteMutex_.lock();
calculating_ = false;
failed_ = true;
calculated_ = false;
readWriteMutex_.unlock();
waitMutex_.unlock();
condVariable_.notify_all();
throw;
}
} else {
// start a non blocking wait until calculation is ready
readWriteMutex_.unlock();
boost::mutex::scoped_lock lock(condMutex_);
condVariable_.wait(lock);
if (failed_)
throw std::exception();
else
return;
}
}
// no need to calculate
readWriteMutex_.unlock();
}
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 程序在 C++11 中使用条件变量进入死锁
- 一个线程提升的死锁
- 如何理解C++并发操作中同类实例导致死锁的问题
- c++ Boost线程.如何使用递归尝试锁?死锁发生在并发代码中
- 并发队列使用Qt是死锁