c++ - 在 C++11 的析构函数中锁定互斥量

标签 c++ thread-safety destructor raii exception-safety

我有一些代码需要线程安全和异常安全。下面的代码是我的问题的一个非常简化的版本:

#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 保护。

在正常情况下,获取标准互斥锁并设置标志的成本仅为一次原子操作。如果不能立即锁定标准互斥锁,线程将进入休眠状态。

如果由于任何原因标准互斥量抛出,互斥量将进入其自旋模式。发生异常的线程将循环直到它可以设置标志。由于没有其他线程知道该线程完全绕过标准互斥锁,因此它们也可以自旋。

在最坏的情况下,这种锁定机制会降级为自旋锁。大多数情况下,它的 react 就像一个普通的互斥体。

关于c++ - 在 C++11 的析构函数中锁定互斥量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16568990/

相关文章:

arrays - 迭代时从数组中删除项目的安全方法?

php - 在析构时要求文件

c++ - 我什么时候应该防止隐式破坏?它是如何工作的?

c++ - Eigen 垂直堆叠行成矩阵

c++ - 如何获得调用带有特定参数的函数的信号?

java - handler.sendMessage() 未到达 android 中的handleMessage()

c++ - 为什么我的程序在析构函数抛出异常时终止?

c++ - GCC 使用 jsoncpp 推断传值复制赋值

c++ - 何时调用 QApplication 析构函数

c# - 线程并发数据库查询是否安全?