C++ 当被调用的方法使用调用方已经锁定的锁时,如何避免死锁?
当被调用的方法使用调用方已经锁定的锁时,如何避免死锁? 我有一个名为closeUnusedConnections()的方法,它创建了一个C++ 当被调用的方法使用调用方已经锁定的锁时,如何避免死锁?,c++,c++11,mutex,C++,C++11,Mutex,当被调用的方法使用调用方已经锁定的锁时,如何避免死锁? 我有一个名为closeUnusedConnections()的方法,它创建了一个std::unique\u锁,但它的调用者已经使用相同的std::mutex:Foo::m\u myMutex创建了一个std::unique\u锁 调用子例程之前是否必须释放锁 Obs.:我无法集成这两种方法,因为closeUnusedConnections也被独立调用 一些代码: void Foo::closeUnusedConnections() {
std::unique\u锁
,但它的调用者已经使用相同的std::mutex
:Foo::m\u myMutex创建了一个std::unique\u锁
调用子例程之前是否必须释放锁
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
}
void Foo::closeUnusedConnections()
{
std::unique_locklock(m_mtx);
//做事
}
Foo::CommNodePtr Foo::getConnection()
{
std::unique_locklock(m_mtx);
//做事
if(true/*某些条件*/)
{
lock.unlock();//目标是避免这种解锁
//有人可能会在两人之间找到锁
//这将解锁,直到关闭未使用的连接的锁。
关闭未使用的连接();
}
//等
}
创建一个私有函数,该函数可以在不抓住锁的情况下完成工作
//call with m_mtx locked
void Foo::unlockedCloseUnusedConnections()
{
// do stuff
}
你的公共职能刚刚开始
void Foo::closeUnusedConnections()
{
std::unique_lock< std::mutex > lock( m_mtx );
unlockedCloseUnusedConnections();
}
void Foo::closeUnusedConnections()
{
std::unique_locklock(m_mtx);
解除锁定CloseUnusedConnections();
}
您的
Foo::getConnection()
函数调用unlockedCloseUnusedConnections()
,因为它已经获得了锁。我认为在单个所有者线程之外有一个对象的访问器是非常危险的,因为该对象可以被其他线程删除、关闭。互斥锁应该在它们拥有另一个线程时防止这些事情发生
nos的解决方案在您描述的方面对您有效,但我觉得您最终会遇到其他问题,因为在调用getConnection
后,当一个线程正在被另一个线程使用时,没有任何东西可以阻止一个线程关闭连接
我有点想知道,如果调用getConnection
,调用可能会返回一个已关闭的连接,那么您试图实现什么
我的建议是重新考虑您的工作流程,以便在任何给定时刻只有一个线程可以访问您的端口,即使这意味着只有一个线程可以使用端口,而其他线程必须发出工作请求。这种“我的类拥有一个互斥锁,它在所有操作上锁定它”的模式很糟糕。它不构成或缩放
使用递归互斥体可以解决一些问题,但两者都有更高的成本,并且不能解决互斥体应该非常狭窄或非常明确的更大问题
互斥是危险的。僵局难以避免。共享状态是一个雷区
一种方法是围绕类编写包装器,如下所示:
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;
};
并且锁定发生在互斥锁中,而不是在Foo
本身中Foo
本身没有任何线程安全性,它自己的调用不会导致问题
这仍然是危险的。互斥体通常是危险的,控制得越不严格,它们就越危险。您可能会使用递归互斥体。但是对你的设计考虑得更仔细一点可能是个更好的主意。为什么一个名为getConnection
的函数会担心关闭未使用的连接呢?该函数只应返回已使用或未使用的连接。我同意杰斯珀·尤尔的观点;重新考虑一下你的设计,互斥锁问题可能就会消失。这里有很多问题。最突出的不是互斥锁,而是一个名为getConnection()
的函数担心未使用的连接。我相信你的建议可能是一个包袱,因为它没有解决设置这两个函数需要的基本设计问题。这部分代码将需要重新设计。closeconnections方法不会关闭实际调用,而是关闭池中的其他长空闲连接。这是一个阻塞呼叫,但重新设计将进入另一个时间框。
foo->*[&](auto& foo){ foo.closeUnusedConnections(); };