在C++11中的析构函数中锁定互斥对象
Locking a mutex in a destructor in C++11
我有一些代码需要线程安全和异常安全。下面的代码是我的问题的一个非常简化的版本:
#include <mutex>
#include <thread>
std::mutex mutex;
int n=0;
class Counter{
public:
Counter(){
std::lock_guard<std::mutex>guard(mutex);
n++;}
~Counter(){
std::lock_guard<std::mutex>guard(mutex);//How can I protect here the underlying code to mutex.lock() ?
n--;}
};
void doSomething(){
Counter counter;
//Here I could do something meaningful
}
int numberOfThreadInDoSomething(){
std::lock_guard<std::mutex>guard(mutex);
return n;}
我有一个互斥锁,我需要将它锁定在对象的析构函数中。问题是我的析构函数不应该抛出异常。
我能做什么?
0(我不能用原子变量代替n
(当然,它在这里会起作用,但这不是我问题的重点(
1( 我可以用旋转锁替换互斥锁
2( 我可以尝试将锁定捕获到一个无限循环中,直到我最终获得锁定,无一例外地引发
这些解决方案似乎都不太吸引人。你有同样的问题吗?你是怎么解决的?
根据Adam H.Peterson的建议,我最终决定编写一个无抛出互斥:
class NoThrowMutex{
private:
std::mutex mutex;
std::atomic_flag flag;
bool both;
public:
NoThrowMutex();
~NoThrowMutex();
void lock();
void unlock();
};
NoThrowMutex::NoThrowMutex():mutex(),flag(),both(false){
flag.clear(std::memory_order_release);}
NoThrowMutex::~NoThrowMutex(){}
void NoThrowMutex::lock(){
try{
mutex.lock();
while(flag.test_and_set(std::memory_order_acquire));
both=true;}
catch(...){
while(flag.test_and_set(std::memory_order_acquire));
both=false;}}
void NoThrowMutex::unlock(){
if(both){mutex.unlock();}
flag.clear(std::memory_order_release);}
这个想法是有两个互斥对象,而不是只有一个。真正的互斥是用std::atomic_flag
实现的自旋互斥。这个旋转互斥被一个可能抛出的std::mutex
保护。
在正常情况下,获取标准互斥锁,并以仅一个原子操作为代价设置标志。如果不能立即锁定标准互斥,那么线程将进入睡眠状态。
如果出于任何原因,标准互斥体抛出,则互斥体将进入其旋转模式。发生异常的线程将循环,直到它可以设置标志为止。由于没有其他线程知道这个线程完全是标准互斥体,所以它们也可能旋转。
在最坏的情况下,这种锁定机制会退化为旋转锁定。大多数时候,它的反应就像一个普通的互斥锁。
这是一个糟糕的情况。您的析构函数正在执行一些可能失败的操作。如果更新此计数器失败将不可恢复地损坏您的应用程序,您可能只想让析构函数抛出。这将通过调用terminate
使应用程序崩溃,但如果应用程序已损坏,则最好终止进程并依赖一些更高级别的恢复方案(例如守护程序的看门狗或另一个实用程序的重试执行(。如果计数器递减失败是可恢复的,则应使用try{}catch()
块吸收异常并恢复(或可能保存信息以供其他操作最终恢复(。如果它是不可恢复的,但不是致命的,您可能需要捕获并吸收异常并记录故障(当然,要确保以异常安全的方式记录(。
如果可以对代码进行重新构造,使析构函数不做任何不会失败的事情,那将是理想的选择。然而,如果您的代码在其他方面是正确的,那么在获取锁时失败的情况可能很少见,除非是在资源受限的情况下,所以吸收或在失败时中止都是可以接受的。对于某些互斥体,lock((可能是一个不抛出操作(例如使用atomic_flag的spinlock(,如果可以使用这样的互斥体,则可以预期lock_guard永远不会抛出。在这种情况下,你唯一担心的就是陷入僵局。
- 当只有一个线程主要使用该对象而其他线程很少使用它时,如何最小化该对象的互斥锁锁定?
- 在任何地方对C++中所有并行线程中的所有锁定和解锁实例使用相同的 std::mutex 和 lock 对象
- 在锁定下清除STD ::映射,而移动到临时对象
- C 删除对象,是否锁定
- 某人如何在一个线程中锁定多个对象
- 同时锁定两个互斥对象
- 如何在同一个线程上用同一个互斥对象锁定两次
- 如果其对象在多个线程中运行,我们是否需要锁定类成员功能
- C++11线程挂起锁定互斥对象
- 何时使用c++类锁定互斥对象
- C++,有没有办法锁定对象本身
- C++(可能还有 Java)如何锁定对象以进行同步
- 像 C# 一样在 Qt 中锁定对象
- 通过私有互斥锁锁定对象的最佳方法是什么
- 在linux上的C/C++中,我如何创建一个预锁定的互斥对象
- 如何为多线程访问实现类锁定对象
- 检查线程在锁定之前是否已经锁定了互斥对象
- 为什么condition_variable没有等待函数,它不重新锁定互斥对象
- 如果一个堆叠的协同程序锁定了一个互斥对象,然后就屈服了,该怎么办
- 锁定shared_ptr对象