2011-11-10 28 views
2

我正在C++中使用boost开发线程安全的惰性对象模式。然而,在这样做的时候,如果我的应用程序有多个线程,我会在LazyObjectcalculate()方法中进入死锁状态。C++ Boost线程。如何使用递归尝试锁定?并发代码中发生死锁

这与某种程度上与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); 
    } 
} 

回答

2

您所提供的一个功能看起来声音。

但是,我强烈怀疑你有锁订购问题。你在一个班级有5个互斥体。你需要保证那些互斥锁是总是被锁定在相同的顺序。否则,你会有一个死锁。

它看起来就像你有一个非常复杂的锁顺序:

  • 5种不同的互斥量
  • 1是递归
  • 至少在试图锁定
+0

其实有一个问题。我没有考虑到这一点。显而易见的情况是,一旦计算出懒惰的对象,并随后调用计算,所有调用都应该返回,因为对象的状态是最新的。在上面的实现中,一些线程进入睡眠状态 – Lauri

0

也许你可以提供描述你想达到什么目的。你没有提供完整的代码,所以只能猜测。

例如,如果一个线程刚成立calculated_true,执行condVariable_.notifyAll()和解锁waitMutex_之前被抢占,然后在condVariable_.wait(lock)就再也没有人另一个线程块终究来唤醒它可以死锁。

我在你写的“互斥体和信号量”的评论中看到,注意条件变量没有内存,它没有像信号量或Windows事件对象。

给这个问题的一个更好的描述,我真的不认为上述这段代码是挽救:)

0

你是正确的,上面的功能没有给整个画面的清晰认识。基本上下面是所有相互作用的功能,争夺*这个资源

我能够减少互斥量只使用3。先决条件是更新方法必须尽可能便宜。

我还有一个关于异常抛出的问题。正如你所看到的,执行performCalculations的计算线程可以抛出异常。如果有一些线程在等待信号继续前进,他们就不能继续,因为甚至发生了异常。是否有可能使用boost来让唤醒线程抛出信号线程中抛出的SAME异常。如果是的话,你能否提供明确的代码这个想法的工作原理?

我的班级需要以下attirbutes。

// 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(); 
}