当被调用方法使用调用方已锁定的同一锁时,如何避免死锁



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

我有一个名为closeUnusedConnections((的方法,它创建了一个std::unique_lock但它的调用者已经创建了一个具有相同std::mutex std::unique_lock: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
}
我相信在

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

nos 的解决方案在您描述的内容中对您有用,但我觉得您最终会遇到其他问题,因为没有什么可以阻止一个线程在调用 getConnection 后被另一个线程使用时关闭连接。

我有点想知道您通过调用getConnection试图完成什么,并且调用实际上可能会返回已关闭的连接。

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

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

//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(),因为它已经锁定了。

"我的类拥有一个互斥锁,它在所有操作上锁定它"的模式很差。 它不会组成或缩放。

使用递归互斥锁补丁可以解决一些问题,但两者都有更高的成本,并且不能解决互斥锁应该非常窄或非常明确的更大问题。

互斥体是危险的。 死锁是难以避免的。 共享状态是一个雷区。

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

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本身没有任何线程安全,它自己的调用也不会引起问题。

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

最新更新