繁体   English   中英

在使用 stop_token 等待 condition_variable_any 时是否需要拥有请求停止的锁?

[英]Is owning the lock required to request a stop while waiting on a condition_variable_any with stop_token?

在等待条件变量时,更改谓词 state 的线程必须拥有锁,因此在唤醒期间不会错过更新。 根据文档,这是必要的,即使在使用原子变量时也是如此。 但是我不确定request_stop()是否已经正确处理了它。

那么问题来了,这两个选项中哪一个是正确且符合标准的?

~jthread()自然不会锁定request_stop() ,但后来我不明白stop_token和原子共享变量之间的区别在哪里。 因此,其中一个需要锁,而另一个不需要。

#include <thread>
#include <condition_variable>
#include <iostream>
#include <chrono>


std::mutex m;
std::condition_variable_any cv;

void waitingThread(std::stop_token st){
    std::unique_lock<std::mutex> lk(m);
    std::cout<<"Waiting"<<std::endl;
    cv.wait(lk, st, [](){return false;});
    std::cout<<"Awake"<<std::endl;
}

void withoutLock(){
    std::jthread jt{waitingThread};
    std::this_thread::sleep_for(std::chrono::seconds(1));
    jt.request_stop();
}

void withLock(){
    std::jthread jt{waitingThread};
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(m);
        jt.request_stop();
    }
}

int main(){
    withoutLock();
    withLock();
}

std::condition_variable指定:

即使共享变量是原子的,它也必须在拥有互斥量的同时被修改才能正确地将修改发布到等待线程。

std::condition_variable_any::waitstd::stop_tokenstd::stop_source不指定,如果等待中断被保证注册停止 state 中的变化。

在等待条件变量时,更改谓词 state 的线程必须拥有锁,因此在唤醒期间不会错过更新。

...不指定,如果等待的中断是保证注册停止state的变化。

问题不完全是缺少更新,而是在notify发生后和再次检查谓词之前回到睡眠状态。

这个答案中的顺序是有问题的。

根据文档,这是必要的,即使在使用原子变量时也是如此。 但是我不确定request_stop()是否已经正确处理了它。

共享的 state 本身同步得很好: thread.stoptoken.intro/5

调用函数request_stopstop_requestedstop_possible不会引入数据竞争。 对返回 true 的request_stop的调用与对返回true的关联stop_tokenstop_source stop_requested上的 stop_requested 的调用同步。

thread.condvarany.intwait中描述的等待谓词循环也会有同样的问题:

while (!stoken.stop_requested()) {
  if (pred())
    return true;
  wait(lock);
}
return pred();

如果在第一行和wait(lock)之间调用request_stop ,则stop_requested()将变为true并且在没有人等待时通知条件变量。 然后我们将开始等待,等待永远不会到来的通知。

如果request_stop只能在wait中释放互斥量时调用,我们将始终保证在再次休眠之前检查stop_requested()

事实上,GNU ISO C++ 库为我们做了这件事: std::condition_variable_any有一个额外的内部互斥锁,用于将request_stop回调(它只是通知请求线程中的 condvar)与等待谓词循环正确同步。 因此,如果该行为是标准所要求的,那么您在这里就不需要自己的互斥锁。

condition_variable_any::wait和定时变体的所有stop_token重载,注册一个stop_callback ,它捕获 Lockable object。然后在request_stop()上执行回调,获取锁并通知等待线程,从而消除丢失的更新。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM