如何使用lock_guard在c++11中实现scoped_lock功能
How to implement scoped_lock functionality in c++11 using lock_guard
看起来c++17中的scoped_lock提供了我想要的功能,但我目前与c++11绑定。
目前,当我们多次用同一个互斥对象调用guard_lock时,我看到了它的死锁问题。scoped_lock是否可以防止多次调用(即重新进入?(?在带有lock_guard的c++11中有这样做的最佳实践吗?
mutex lockingMutex;
void get(string s)
{
lock_guard<mutex> lock(lockingMutex);
if (isPresent(s))
{
//....
}
}
bool isPresent(string s)
{
bool ret = false;
lock_guard<mutex> lock(lockingMutex);
//....
return ret;
}
要能够多次锁定同一互斥对象,需要使用std::recursive_mutex。递归互斥比非递归互斥更昂贵。
不过,最好的做法是设计代码,使线程不会多次锁定同一互斥对象。例如,让公共函数先锁定互斥体,然后调用期望互斥体已经锁定的实现函数。实现函数不能调用锁定互斥对象的公共API函数。例如:
class A {
std::mutex m_;
int state_ = 0;
private: // These expect the mutex to have been locked.
void foo_() {
++state_;
}
void bar_() {
this->foo_();
}
public: // Public functions lock the mutex first.
void foo() {
std::lock_guard<std::mutex> lock(m_);
this->foo_();
}
void bar() {
std::lock_guard<std::mutex> lock(m_);
this->bar_();
}
};
作用域锁不能提供您想要的功能。
Scoped锁只是锁保护的一个变体。它之所以存在,是因为将锁保护更改为可变模板时存在一些ABI问题。
要拥有可重入互斥,您需要使用一个可重入的互斥。但这在运行时成本更高,而且通常表明您的互斥对象状态不小心。在持有互斥对象时,您应该完全理解正在执行的所有其他同步操作。
一旦您完全理解了正在执行的所有同步操作,就很容易避免递归锁定。
这里有两种模式可以考虑。首先,将公共锁定API与私有非锁定API分离。其次,将同步与实现分离。
private:
mutex lockingMutex;
bool isPresent(string s, lock_guard<mutex> const& lock) {
bool ret = false;
//....
return ret;
}
void get(string s, lock_guard<mutex> const& lock) {
if (isPresent(s, lock))
{
//....
}
}
public:
void get(string s) {
return get( std::move(s), lock_guard<mutex>(lockingMutex) );
}
bool isPresent(string s) {
return isPresent( std::move(s), lock_guard<mutex>(lockingMutex) );
}
};
这里我用lock_guard<mutex>
作为"我们有锁的证据"。
一个通常更好的选择是将类编写为非线程安全的,然后使用包装器:
template<class T>
struct mutex_guarded {
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<std::decay_t<T0>, mutex_guarded>{}, bool> =true
>
mutex_guarded(T0&&t0, Ts&&...ts):
t( std::forward<T0>(t0), std::forward<Ts>(ts)... )
{}
mutex_guarded()=default;
~mutex_guarded=default;
template<class F>
auto read( F&& f ) const {
auto l = lock();
return f(t);
}
template<class F>
auto write( F&& f ) {
auto l = lock();
return f(t);
}
private:
auto lock() { return std::unique_lock<std::mutex>(m); }
auto lock() const { return std::unique_lock<std::mutex>(m); }
mutable std::mutex m;
T t;
};
现在我们可以这样使用:
mutex_guarded<Foo> foo;
foo.write([&](auto&&foo){ foo.get("hello"); } );
您可以编写mutex_gaurded
、shared_mutex_guarded
、not_mutex_guarded
甚至async_guarded
(它返回未来并序列化工作线程中的操作(。
只要类在方法中不离开自己的"控制区",这种模式就可以更容易地编写互斥体保护的数据,并且可以将相关的互斥体保护数据组合到一个包中,而不必重写它们。
- 如果没有malloc,链表实现将失败
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 实现无开销push_back的最佳方法是什么
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 如何正确实现和访问运算符的各种自定义枚举器
- C++Union/Struct位域的实现和可移植性
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 在c++中实现LinkedList时,应出现未处理的错误
- 为左值和右值的包装器实现C++范围
- 使用模板进行堆栈实现; "name followed by :: must be a class or namespace"
- 使用GSoap实现ONVIF
- 在用于格式4的arm模拟器中实现功能时的一个问题
- 用于AVX的ln(x)的实现,m256
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在C++中,如何在类和函数(可能是模板化的)的头中编写完整的实现
- 不同D3DPOOL实现的Direct3D VertexBuffer Lock()和Unlock()函数