当被调用方法使用调用方已经锁定的相同锁时,如何避免死锁?
我有一个名为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
本身没有任何线程安全,它自己的调用也不会引起问题。
这仍然很危险。 互斥体通常是危险的,你控制得越不严格,它们就越危险。