[英]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
而崩溃,但如果您的应用程序已损坏,则最好terminate
该进程并依赖某些更高级别的恢复方案(例如守护程序的守护程序或重试另一个实用程序的执行) 。 如果计数器的递减失败是可恢复的,则应使用try{}catch()
块吸收异常并恢复(或者可能保存其他操作的信息以最终恢复)。 如果它不可恢复,但它不是致命的,你可能想要捕获并吸收异常并记录失败(当然,确保以异常安全的方式登录)。
如果可以重构代码以使析构函数不执行任何不能失败的操作,那将是理想的。 但是,如果您的代码在其他方面是正确的,那么获取锁定时失败的情况可能很少,除非资源受限,因此无论是吸收还是中止失败都可能是可以接受的。 对于某些互斥锁,lock()可能是一个无抛出操作(例如使用atomic_flag的自旋锁),如果你可以使用这样的互斥锁,你可以期望lock_guard永远不会抛出。 在这种情况下你唯一的担忧就是陷入僵局。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.