我有一些代码需要线程安全和异常安全。下面的代码是我的问题的一个非常简化的版本:
#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
这可能会抛出。
在正常情况下,获取标准互斥体并设置标志仅需要一次原子操作的成本。如果标准互斥体不能立即锁定,线程将进入休眠状态。
如果由于任何原因标准互斥体抛出异常,互斥体将进入其旋转模式。发生异常的线程将循环,直到它可以设置标志。由于没有其他线程知道该线程完全绕过了标准互斥锁,因此它们也可能会旋转。
在最坏的情况下,这种锁定机制会降级为自旋锁。大多数时候它的反应就像普通的互斥体一样。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)