C++ 如何使用std::jthread安全地注册std::stop_回调
对于如何安全地为C++ 如何使用std::jthread安全地注册std::stop_回调,c++,multithreading,c++20,C++,Multithreading,C++20,对于如何安全地为jthread注册回调,我有点困惑。您需要标记,这意味着您需要在创建jthread之后进行注册,这意味着回调将在jthread之前被销毁。在下面的示例中,cb5和cb6显然会在jthread的dtor启动之前被销毁,因此它们会自动取消注册,并且永远不会执行。相反地,cb1和cb2在jthread销毁后被显式销毁,因此它们保证作为请求停止的数据的副作用执行。现在令人困惑的是,我找不到任何保证cb3和cb4能够保证执行的保证。我找不到任何东西表明请求stop会自动更改设置stop标志
jthread
注册回调,我有点困惑。您需要标记
,这意味着您需要在创建jthread
之后进行注册,这意味着回调将在jthread
之前被销毁。在下面的示例中,cb5
和cb6
显然会在jthread
的dtor启动之前被销毁,因此它们会自动取消注册,并且永远不会执行。相反地,cb1
和cb2
在jthread
销毁后被显式销毁,因此它们保证作为请求停止的数据的副作用执行。现在令人困惑的是,我找不到任何保证cb3
和cb4
能够保证执行的保证。我找不到任何东西表明请求stop会自动更改设置stop标志并执行所有回调。另一方面,我查看了request\u stop
的函数,它似乎执行以下操作
- 带锁
- 设置停止标志并获取上次注册的停止回调
- 松开锁
- 运行回调
- 释放二进制信号量(该回调的析构函数等待该信号量)
- 尝试重新获取锁以执行下一个回调
cb3
和cb4
的执行正在与其析构函数竞争(至少是cb3
,因为cb4
将被选择在用于设置停止标志的同一个锁下执行,但我同样找不到此处提到的cb4
的保证).那么,如果不显式调用request\u stop
,如何在jthread
中正确使用stop\u回调
?您不能在之后注册回调,因为它们会在之前被销毁,比如cb5
和cb6
,您也不能在线程中注册它们,因为它们不能保证执行e就像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;
}
#包括
#包括
#包括
#包括
int main(int argc,const char*const*const argv)
{
使用名称空间std::chrono_文本;
const auto_cb1=[]()->void{
std::cout void{
标准::cout标准:
对返回true的请求停止
的调用与对返回true的相关停止令牌
或停止源
对象的请求停止
的调用同步
这意味着对所请求的stop\u
的调用会在之后返回true
任何返回true
的request\u stop
。因此,在调用request\u stop
实际将true
返回到某个线程之前,您的while
循环无法退出。更重要的是,在此类调用返回之前,它无法退出
请求停止
是为了:
如果发出请求,则同步调用关联的stop\u callback
对象注册的回调
被“同步地”调用意味着:这个函数要么在请求停止的线程上调用它们,要么与它们被调用的任何线程同步。主要的一点是,在这些回调完成之前,这个函数不会返回
在该函数返回之前,stop\u requested
将不会返回true
,如前所述
因此不存在数据竞争。在线程退出之前,回调不会被销毁。“我找不到任何东西表明请求停止会自动更改设置停止标志并执行所有回调,例如。”嗯,你看了哪里?可能我不够清楚。它到底说它在哪里以原子方式设置标志并在同一个原子操作中执行所有回调?如果它说“以原子方式”又有什么关系或者不是;它与请求的stop\u
调用同步。它说这样的调用不会引入数据竞争。谢谢你的澄清。听起来很有说服力,我只是试图用代码来理解它,但仍然不能完全理解它。所以在第264行和第270行之间,锁被释放,而解构函数被释放当前回调被信号量解除阻塞,因此下一个回调的析构函数能否快速获取锁并注销自身并完成销毁过程?停止请求位设置在232中,检查停止请求函数的线程是否无法直接观察到?我的意思是,此函数所做的一切只是检查此位是否为s