当被调用方法使用与调用者已锁定的锁相同的锁时,如何避免死锁?

时间:2017-03-31 17:10:51

标签: c++ c++11 mutex

调用方法使用与调用方已锁定的锁相同的锁时,如何避免死锁?

我有一个名为 closeUnusedConnections()的方法,它创建了一个std::unique_lock但是它的调用者已经创建了一个std::unique_lock std::mutex:Foo :: m_myMutex。

在调用子程序之前,我是否必须释放锁?

Obs。:我无法集成这两种方法,因为 closeUnusedConnections 也是独立调用的。

一些代码:

void Foo::closeUnusedConnections()
{   
   std::unique_lock< std::mutex > lock( m_mtx );
   // do stuff
}

Foo::CommNodePtr Foo::getConnection()
{   
   std::unique_lock< std::mutex > lock( m_mtx );
   // do stuff
   if( true /*some condition*/ )
   {
      lock.unlock();     // The goal is to avoid this unlock
                         // someone could get the lock between
                         // this unlock until closeUnusedConnections's lock.
      closeUnusedConnections(); 
   }
   // etc
}

3 个答案:

答案 0 :(得分:1)

我认为在单个所有者线程之外拥有一个对象的访问器是非常危险的,在这种情况下,该对象可以被其他线程删除,关闭。互斥锁被认为可以防止这些事情发生在他们拥有另一个线程时。

nos的解决方案在你描述的内容中对你有用但我觉得你最终会遇到其他问题因为在调用之后没有什么能阻止一个线程关闭连接而另一个线程正在使用它getConnection

我有点想知道你试图通过调用getConnection来实现什么,因为调用可能实际上会返回一个已关闭的连接。

我的建议是重新考虑您的工作流程,以便在任何给定时刻只有一个线程可以访问您的端口,即使这意味着只有一个线程可以使用该端口,而其他线程必须提出工作请求。

答案 1 :(得分:0)

创建一个私有函数,无需抓住锁即可完成工作

//call with m_mtx locked
void Foo::unlockedCloseUnusedConnections()
{     
   // do stuff
}

您的公共功能刚刚变为

 void Foo::closeUnusedConnections()
 {
     std::unique_lock< std::mutex > lock( m_mtx );
     unlockedCloseUnusedConnections();
 }

您的Foo::getConnection()函数调用了unlockedCloseUnusedConnections(),因为它已经锁定了。

答案 2 :(得分:0)

“我的班级拥有互斥锁,它将其锁定在所有操作上”的模式很糟糕。它不构成或缩放。

在某些问题上使用递归互斥补丁,但两者都有更高的成本,并且无法解决互斥量应该非常狭窄或极其明显的大问题。

互斥体很危险。僵局很难避免。共享国家是一个雷区。

一种方法是围绕类编写一个包装器:

template<class T>
struct mutex_guarded {
  template<class F>
  auto operator->*( F&& f )
  -> decltype( std::forward<F>(std::declval<T&>()) )
  {
    std::unique_lock<std::mutex> l(m_mtx);
    return std::forward<F>(f)(t);
  }
  template<class F>
  auto operator->*( F&& f ) const
  -> decltype( std::forward<F>(std::declval<T const&>()) )
  {
    std::unique_lock<std::mutex> l(m_mtx);
    return std::forward<F>(f)(t);
  }
  mutex_guarded(T tin):t(std::move(tin)) {}
  T copy_out() const {
    return (*this)->*[](auto& t){ return t; };
  }
  T move_out() {
    return (*this)->*[](auto& t){ return std::move(t); };
  }
  template<class U>
  void assign(U&& u) {
    return (*this)->*[&u](auto& t) { t = std::forward<U>(u); }
  }
  mutex_guarded(T const& tin):t(tin) {}
  mutex_guarded(T && tin):t(std::move(tin)) {}
  mutex_guarded(mutex_guarded const& tin):
    mutex_guarded(tin.copy_out())
  {}
  mutex_guarded(mutex_guarded && tin):
    mutex_guarded(tin.move_out())
  {}
  mutex_guarded& operator=(T const& tin) {
    assign( tin );
    return *this;
  }
  mutex_guarded& operator=(T&& tin) {
    assign( std::move(tin) );
    return *this;
  }
  mutex_guarded& operator=(mutex_guarded const& tin) {
    return *this = tin.copy_out();
  }
  mutex_guarded& operator=(mutex_guarded&& tin) {
    return *this = tin.move_out();
  }
private:
  std::mutex m_mtx;
  T t;
};

复制/移动/构建的一些奖励增加。

现在可以通过

mutex_guarded<Foo> foo进行互动
foo->*[&](auto& foo){ foo.closeUnusedConnections(); };

并且锁定发生在mutex_guarded,而不是Foo本身。 Foo生活中没有任何线程安全本身,并且它自己的调用不会导致问题。

这仍然很危险。互斥体通常是危险的,你控制它们越不紧密就越危险。