[英]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.