[英]how to safely register std::stop_callback with std::jthread
我对如何安全地为jthread
注册回调有点困惑。 您需要token
,这意味着您需要在创建jthread
之后进行注册,这意味着回调将在jthread
之前被销毁。 在下面的示例中, cb5
和cb6
显然在 jthread 的jthread
启动之前被销毁,因此它们会自动注销自己并且永远不会执行。 相反, cb1
和cb2
在jthread
销毁后被显式销毁,因此可以保证它们作为 dtor 请求停止的副作用而执行。 现在令人困惑的部分是我找不到任何保证cb3
和cb4
可以执行的保证。 例如,我找不到任何东西说请求停止会自动更改设置停止标志并执行所有回调。 另一方面,我查看了request_stop
的Implementation:223 ,似乎它执行以下操作
现在终于到了这个问题,根据上述, cb3
和cb4
的执行正在与它们的析构函数竞争(至少cb3
,因为cb4
将被选择在用于设置停止标志的同一锁下执行,但又一次我找不到某处提到的cb4
的保证)。 那么如何在不显式调用request_stop
的情况下正确使用stop_callback
的jthread
呢? 您不能在之后注册回调,因为它们会像cb5
和cb6
一样在之前被销毁,并且您不能在线程中注册它们,因为它们不能保证像cb3
和cb4
那样执行,而且我不认为它打算给它们以cb1
和cb2
的复杂方式延长使用寿命
#include <chrono>
#include <iostream>
#include <stop_token>
#include <thread>
int main(int argc, const char * const * const argv)
{
using namespace std::chrono_literals;
const auto _cb1 = []() -> void {
std::cout << "cb1\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
};
const auto _cb2 = []() -> void {
std::cout << "cb2\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
};
using CB1 =
decltype(std::stop_callback(std::declval<std::stop_token>(), _cb1));
using CB2 =
decltype(std::stop_callback(std::declval<std::stop_token>(), _cb2));
std::byte storage1[sizeof(CB1)];
std::byte storage2[sizeof(CB2)];
const CB1 * cb1 = nullptr;
const CB2 * cb2 = nullptr;
{
std::jthread worker([](const std::stop_token & stop_token) {
std::stop_callback cb3(stop_token, [] {
std::cout << "cb3\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
});
std::stop_callback cb4(stop_token, [] {
std::cout << "cb4\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
});
while (!stop_token.stop_requested())
{
}
});
cb1 = new (&storage1) std::stop_callback(worker.get_stop_token(), _cb1);
cb2 = new (&storage2) std::stop_callback(worker.get_stop_token(), _cb2);
std::stop_callback cb5(worker.get_stop_token(), [] {
std::cout << "cb5\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
});
std::stop_callback cb6(worker.get_stop_token(), [] {
std::cout << "cb6\n";
std::this_thread::yield();
std::this_thread::sleep_for(10s);
});
std::this_thread::sleep_for(2s);
}
cb1->~CB1();
cb2->~CB2();
return 0;
}
该标准直接指出:
返回 true 的
request_stop
调用与返回 true 的关联stop_token
或stop_source
stop_requested
上的 stop_requested 调用同步。
这意味着对返回true
的stop_requested
的调用“发生在”任何返回true
的request_stop
之后。 因此,您的while
循环无法退出,直到对request_stop
的调用实际上将true
返回给某个线程。 更重要的是,在这样的调用返回之前它不能退出。
request_stop
明确声明为:
如果发出请求,则同步调用关联的
stop_callback
对象注册的回调。
被“同步”调用意味着:这个 function 要么在请求停止的线程上调用它们,要么与它们被调用的任何线程同步。 主要的一点是,在这些回调完成之前,这个 function 不会返回。
并且在此 function 返回之前, stop_requested
不会返回true
,如前所述。
所以不存在数据竞赛。 在线程退出之前,回调不会被销毁。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.