在我的代碼中,我想使用 astd::atomic_flag來同步兩個執行緒。具體而言,我想使用新的wait和notify_all被用C 20引入的功能。
簡而言之:一個執行緒正在等待標志準備就緒,而另一個執行緒將設定標志并發出通知。然而,問題在于atomic_flag堆疊中的生命周期將在通知后被銷毀,而第一個執行緒可能仍在呼叫wait.
基本上,我有一些相當于以下代碼段的內容:
#include <atomic>
#include <thread>
int main(int, char**)
{
auto t = std::thread{};
{
auto f = std::atomic_flag{};
t = std::thread{[&f] { f.wait(false); }};
// Ensures that 't' is waiting on 'f' (not 100% guarantee, but you get the point)
std::this_thread::sleep_for(std::chrono::milliseconds{50});
f.test_and_set();
f.notify_all();
} // <--- 'f' is destroyed here but 't' may still be in the wait call
t.join();
return 0;
}
過去,我曾boost::latch在這樣的情況下使用過,并且從經驗中我知道這種模式幾乎總是會崩潰或斷言。但是,替換boost::latchwithstd::atomic_flag不會導致任何崩潰、斷言或死鎖。
我的問題:std::atomic_flag在呼叫之后銷毀 a 是否安全notify_all(即喚醒執行緒可能仍在wait方法中)?
uj5u.com熱心網友回復:
不,這不安全。
從標準
在標準([atomics.flag])中, 的作用atomic_flag_wait描述如下:
效果:依次重復執行以下步驟:
- 評估
flag->test(order) != old.- 如果該評估的結果是
true,則回傳。- 阻塞直到它被原子通知操作解除阻塞或被虛假地解除阻塞。
這意味著,在解除阻塞后,std::atomic_flag訪問 以讀取新值。因此,這是從另一個執行緒破壞原子標志的競賽。
在實踐中
可能,代碼片段作業正常,因為 的解構式std::atomic_flag是微不足道的。所以記憶體在堆疊上保持完整,等待執行緒仍然可以繼續使用這些位元組,就好像它們是原子標志一樣。
通過稍微修改代碼以顯式地將所std::atomic_flag居住的記憶體歸零,該代碼段現在會死鎖(至少在我的系統上)。
#include <atomic>
#include <cstddef>
#include <cstring>
#include <thread>
int main(int, char**)
{
auto t = std::thread{};
// Some memory to construct the std::atomic_flag in
std::byte memory[sizeof(std::atomic_flag)];
{
auto f = new (reinterpret_cast<std::atomic_flag *>(&memory)) std::atomic_flag{};
t = std::thread{[&f] { f->wait(false); }};
std::this_thread::sleep_for(std::chrono::milliseconds{50});
f->test_and_set();
f->notify_all();
f->~atomic_flag(); // Trivial, but it doesn't hurt
// Set the memory where the std::atomic_flag lives to all zeroes
std::memset(&memory, 0, sizeof(std::atomic_flag));
}
t.join();
return 0;
}
如果在將記憶體設定為全零后碰巧讀取原子標志的值,這將使等待執行緒死鎖(可能是因為它現在將這些零解釋為原子標志值的“假”)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/393747.html
下一篇:如何轉換長日期格式
