c++ - std::condition_variable::notify_one 是可重入的吗?

标签 c++ signals atomic condition-variable sigint

我可以安全地执行以下代码吗?是否有可能在这里出现死锁或一些意外行为,尤其是当 SIGINT 到达时?

#include <atomic>
#include <condition_variable>
#include <csignal>

std::mutex m;
std::condition_variable cv;
std::atomic<bool> flag(false);

void f1(){
             std::signal(SIGTERM, [](int signal){flag.store(true, 
             std::memory_order_release); cv.notify_one();});//register for signal
             std::unique_lock<std::mutex> mtx(m);
             cv.wait(mtx, []{return flag.load(std::memory_order_consume);});//wait for signal or f2() notify
         }

void f2(){
             flag.store(true, std::memory_order_release);
             cv.notify_one();
         }

int main(){
    std::thread th1(f1);
    std::thread th2(f2);
    th1.join();
    th2.join();
    return 0;
}

最佳答案

pthread 函数和使用它们的 std::condition_variablestd::mutex异步的信号安全。请参阅 man signal-safety(7) 中的异步信号安全函数列表.


有点题外话:如果您在更新 flag 时没有锁定互斥量,则会导致错过通知。想象一下场景:

Thread 1        | Thread 2
                | mutex.lock()
                | flag == false
                | cv.wait(mtx, []{return flag;}):
                |     while(!flag)
flag = true     | 
cv.notify_one() | <--- this notification is lost
                |         cv.wait(mtx); <--- waits for the next notification           

这是一个非常微妙但非常常见的编程错误。当经常通知条件变量时,很难注意到丢失的通知和引入的延迟。只有当条件变量只通知一次时,问题才可能变得明显。

条件变量没有状态,它们只是一种(嘈杂的)通知机制,与 Windows 事件不同,这是一个混淆因素。

在更新 flag 时锁定互斥体可以解决这个问题。


如果您想在收到信号时通知条件变量,请创建一个专用于信号处理的线程。示例:

#include <condition_variable>
#include <iostream>
#include <thread>
#include <signal.h>

std::mutex m;
std::condition_variable cv;
bool flag = false;

void f1(){
    std::unique_lock<std::mutex> lock(m);
    while(!flag)
        cv.wait(lock);
}

void signal_thread() {
    sigset_t sigset;
    sigfillset(&sigset);
    int signo = ::sigwaitinfo(&sigset, nullptr);
    if(-1 == signo)
        std::abort();
    std::cout << "Received signal " << signo << '\n';

    m.lock();
    flag = true;
    m.unlock();
    cv.notify_one();
}

int main(){
    sigset_t sigset;
    sigfillset(&sigset);
    ::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);

    std::thread th1(f1);
    std::thread th2(signal_thread);

    th1.join();
    th2.join();
}

请注意,信号必须在所有线程中被阻塞,以便只有信号处理程序线程接收这些信号。

关于c++ - std::condition_variable::notify_one 是可重入的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52424036/

相关文章:

具有不同参数的 C++ boost 信号和插槽?

unix - 通过将 ctrl-c 发送到标准输入来将 SIGINT 发送到进程

Redis 中具有读取操作的事务

c++ - Qt:将信号连接到具有更多参数的插槽

c# - 将 C# 委托(delegate)传递给 C++/CLI 包装器

c++ - 具有友元函数的模板类的链接器错误

c++ - FORTIFY_SOURCE 和 Og 优化级别

c++ - 在 Folly 的无锁 SPSC 队列中使用 std::memory_order_consume

python - python有原子CompareAndSet操作吗?

c++ - 多线程环境中的智能指针