如果condition_variable是一个类的成员,我的理解是:
根据这些预期,我的问题是:下面的示例代码为什么不通知等待线程?
#include <mutex>
#include <condition_variable>
#define NOTIFY_IN_DESTRUCTOR 
struct notify_on_delete {
    std::condition_variable cv;
    ~notify_on_delete() {
#ifdef NOTIFY_IN_DESTRUCTOR
        cv.notify_all();
#endif
    }
};
int main () {
    for (int trial = 0; trial < 10000; ++trial) {
        notify_on_delete* nod = new notify_on_delete();
        std::mutex flag;
        bool kill = false;
        std::thread run([nod, &flag, &kill] () {
            std::unique_lock<std::mutex> lock(flag);
            kill = true;
            nod->cv.wait(lock);
        });
        while(true) {
            std::unique_lock<std::mutex> lock(flag);
            if (!kill) continue;
#ifdef NOTIFY_IN_DESTRUCTOR
            delete nod;
#else
            nod->cv.notify_all();
#endif
            break;
        }
        run.join();
#ifndef NOTIFY_IN_DESTRUCTOR
        delete nod;
#endif
    }
    return 0;
}在上面的代码中,如果没有定义NOTIFY_IN_DESTRUCTOR,那么测试将可靠地运行到完成。然而,当定义NOTIFY_IN_DESTRUCTOR时,测试将随机挂起(通常在几千次试验之后)。
我正在使用Apple : Apple版本9.0.0 (clang-900.0.39.2)进行编译,目标: x86_64-apple-darwin17.3.0线程模型: posix指定的C++14,使用设置的调试标志进行编译。
编辑:
为了澄清:这个问题是关于condition_variable实例的指定行为的语义。上面的第二点似乎在下面的报价中被重新执行
封锁要求:不会有线程阻塞*这。 注意事项:也就是说,所有线程都应已被通知;它们随后可能会阻塞等待中指定的锁。这就放松了通常的规则,这就要求所有的等待呼叫发生在毁灭之前。只有解除阻塞的通知才需要在销毁之前发生。用户应该注意确保在析构函数启动后没有线程等待*这一点,特别是当等待线程在循环中调用等待函数或使用接受谓词的wait、wait_for或wait_until的重载时。- end注意事项
核心的语义问题似乎是“阻止”的意思。我现在对上述引文的解释是,在行之后
cv.notify_all(); // defined NOTIFY_IN_DESTRUCTOR在~notify_on_delete()中,线程测试没有在nod上被“阻塞”--也就是说,我目前理解在调用“取消阻塞等待的通知”之后发生了,因此根据引号,已经满足了继续销毁condition_variable实例的要求。
有人能澄清“阻塞的”或“取消阻塞的通知”,大意是在上面的代码中,对notify_all()的调用不符合~condition_variable()的要求吗?
发布于 2018-01-04 11:18:19
定义NOTIFY_IN_DESTRUCTOR时:
调用notify_one()/notify_all()并不意味着等待线程立即被唤醒,当前线程将等待另一个线程。这只是意味着,如果等待线程在当前线程调用通知之后的某个时刻醒来,它应该继续进行。因此,本质上,您可能是在等待线程唤醒之前删除条件变量(取决于线程的调度方式)。
对于为什么挂起的解释,即使在其他线程等待时删除了条件变量,也取决于等待/通知操作是使用与条件变量关联的队列实现的。这些队列保存等待条件变量的线程。释放条件变量将意味着摆脱这些线程队列。
https://stackoverflow.com/questions/48093715
复制相似问题