[英]how to safely register std::stop_callback with std::jthread
I'm a bit confused about how to safely register callbacks for jthread
.我对如何安全地为jthread
注册回调有点困惑。 You need the token
so that means that you would need to do the registration after creating the jthread
which means that the callback would be destroyed before the jthread
.您需要token
,这意味着您需要在创建jthread
之后进行注册,这意味着回调将在jthread
之前被销毁。 In the example below cb5
and cb6
obviously get destroyed before the dtor of the jthread
starts so they automatically deregister themselves and never execute.在下面的示例中, cb5
和cb6
显然在 jthread 的jthread
启动之前被销毁,因此它们会自动注销自己并且永远不会执行。 Conversely, cb1
and cb2
are explicitly destroyed after the destruction of jthread
so they are guaranteed to execute as a side effect to the dtor requesting a stop.相反, cb1
和cb2
在jthread
销毁后被显式销毁,因此可以保证它们作为 dtor 请求停止的副作用而执行。 Now the confusing part is that I can not find any guarantees that cb3
and cb4
are guaranteed to execute.现在令人困惑的部分是我找不到任何保证cb3
和cb4
可以执行的保证。 There is nothing I could find that says that requesting stop would atomically change set the stop flag and execute all the callbacks for example.例如,我找不到任何东西说请求停止会自动更改设置停止标志并执行所有回调。 On the other hand, I looked at the Implementation:223 of request_stop
and it seems that it does the following另一方面,我查看了request_stop
的Implementation:223 ,似乎它执行以下操作
Now finally to the question, according to the above, the execution of cb3
and cb4
is racing with their destructors (at least cb3
, because cb4
will be chosen for execution under the same lock that will be used to set the stop flag, but again I could not find that guarantee for cb4
mentioned somewhere).现在终于到了这个问题,根据上述, cb3
和cb4
的执行正在与它们的析构函数竞争(至少cb3
,因为cb4
将被选择在用于设置停止标志的同一锁下执行,但又一次我找不到某处提到的cb4
的保证)。 So how can one use a stop_callback
properly with a jthread
without calling request_stop
explicitly?那么如何在不显式调用request_stop
的情况下正确使用stop_callback
的jthread
呢? you can't register the callbacks after because they will be destroyed before like cb5
and cb6
and you can't register them withing the thread because they are not guaranteed to execute like cb3
and cb4
and I do not think that it was intended to give them a longer lifetime in the convoluted way of cb1
and cb2
您不能在之后注册回调,因为它们会像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;
}
The standard directly states that :该标准直接指出:
A call to
request_stop
that returns true synchronizes with a call tostop_requested
on an associatedstop_token
orstop_source
object that returns true.返回 true 的request_stop
调用与返回 true 的关联stop_token
或stop_source
stop_requested
上的 stop_requested 调用同步。
This means that a call to stop_requested
that returns true
"happens after" any request_stop
that returns true
.这意味着对返回true
的stop_requested
的调用“发生在”任何返回true
的request_stop
之后。 So your while
loop cannot exit until a call to request_stop
actually returns true
to some thread.因此,您的while
循环无法退出,直到对request_stop
的调用实际上将true
返回给某个线程。 More importantly, it cannot exit until such a call returns .更重要的是,在这样的调用返回之前它不能退出。
request_stop
is explicitly stated to: request_stop
明确声明为:
If the request was made, the callbacks registered by associated
stop_callback
objects are synchronously called.如果发出请求,则同步调用关联的stop_callback
对象注册的回调。
Being called "synchronously" means exactly that: this function will either call them on the thread requesting the stop or it will synchronize with whatever thread(s) they do get called on.被“同步”调用意味着:这个 function 要么在请求停止的线程上调用它们,要么与它们被调用的任何线程同步。 The main point being that until those callbacks are finished, this function does not return.主要的一点是,在这些回调完成之前,这个 function 不会返回。
And until this function returns, stop_requested
will not return true
, as previously stated.并且在此 function 返回之前, stop_requested
不会返回true
,如前所述。
So there is no data race.所以不存在数据竞赛。 The callbacks will not be destroyed before the thread exits.在线程退出之前,回调不会被销毁。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.