Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/visual-studio-2010/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C+中锁定析构函数中的互斥体+;11_C++_Thread Safety_Destructor_Raii_Exception Safety - Fatal编程技术网

C++ 在C+中锁定析构函数中的互斥体+;11

C++ 在C+中锁定析构函数中的互斥体+;11,c++,thread-safety,destructor,raii,exception-safety,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(){ st

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

#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;}
#包括
#包括
std::互斥互斥;
int n=0;
班级计数器{
公众:
计数器(){
std::锁定保护(互斥);
n++;}
~Counter(){
std::lock_guardguard(mutex);//在这里如何保护mutex.lock()的底层代码?
n--;}
};
无效剂量测定法(){
柜台;
//在这里,我可以做一些有意义的事情
}
int numberOfThreadInDoSomething(){
std::锁定保护(互斥);
返回n;}
我有一个互斥锁,需要锁在对象的析构函数中。问题是我的析构函数不应该抛出异常

我能做什么

0)我不能用一个原子变量替换
n
(当然在这里它会起作用,但这不是我问题的重点)

1) 我可以用旋转锁替换互斥锁

2) 我可以尝试将锁捕获到一个无限循环中,直到我最终获得了锁,并且没有引发任何异常


这些解决方案似乎都不是很吸引人。你也有同样的问题吗?你是怎么解决的?

这是一个很糟糕的情况。您的析构函数正在执行可能失败的操作。如果未能更新此计数器将无法恢复地损坏您的应用程序,您可能希望简单地让析构函数抛出。这将通过调用
terminate
使您的应用程序崩溃,但如果您的应用程序已损坏,则最好终止该进程并依赖某种更高级别的恢复方案(例如守护程序的看门狗或另一个实用程序的重试执行)。如果减量计数器的失败是可恢复的,则应使用
try{}catch()
块和恢复来吸收异常(或可能保存其他操作的信息以最终恢复)。如果它不可恢复,但不是致命的,您可能希望捕获并吸收异常并记录失败(当然,请确保以异常安全的方式登录)


如果代码可以重新构造,使析构函数不会做任何不能失败的事情,那将是理想的。然而,如果您的代码在其他方面是正确的,那么在获取锁时发生故障的情况可能很少,除非是在资源受限的情况下,所以在发生故障时吸收或中止都是可以接受的。对于某些互斥体,lock()可能是一个无抛出操作(例如使用原子_标志的自旋锁),如果您可以使用这样的互斥体,则可以期望lock_guard永远不会抛出。在这种情况下,您唯一担心的是死锁。

正如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_标志实现的自旋互斥体。此旋转互斥体由一个
std::mutex
保护,该互斥体可能会抛出

在正常情况下,只需一次原子操作即可获取标准互斥,并设置标志。如果无法立即锁定标准互斥锁,则线程将进入睡眠状态

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


在最坏的情况下,这种锁定机制会退化为自旋锁。大多数情况下,它的反应就像普通的互斥锁一样。

我有一个互斥锁,需要锁在对象的析构函数中。
——听起来是个坏主意。与其向我们提供解决方案并让我们解决问题,不如告诉我们您要解决的问题,这样我们才能提供更好的解决方案。@RobertHarvey我真正想做的是在将修改保存到数据库中后将其插入共享缓存。