[英]Thread safety for deterministic one-time write to static location?
我正在檢查 x86-64 上的 CPUID 標志以獲取指令集擴展,並將結果緩存在 static 數組中。 執行此操作的 function 可以從多個不同線程中的任何一個調用,因此可能出現競爭條件 - 但這有關系嗎? 任何線程都會產生相同的結果,所以我不關心寫入沖突,並且多次檢查相同的標志是不成問題的。 我的結論是這是安全的,但我錯過了什么嗎?
這是我正在做的精簡版:
enum class Arch : int
{
SSE2,
AVX,
MAX
};
static bool CheckCPUID(Arch arch)
{
// Check CPU features
}
static bool IsSupportedArch(Arch arch)
{
enum class TriVal : int
{
Unknown,
True,
False
};
constexpr int arch_max = static_cast<int>(Arch::MAX);
const int i_arch = static_cast<int>(arch);
// Cache results in-memory:
static TriVal cached_results[arch_max - 1] = { TriVal::Unknown, TriVal::Unknown };
if (cached_results[i_arch] == TriVal::Unknown)
{
cached_results[i_arch] = CheckCPUID(arch) ? TriVal::True : TriVal::False;
}
return cached_results[i_arch] == TriVal::True;
}
所以我不在乎有沖突的寫入
對非原子對象的沖突寫入是數據競爭,數據競爭總是會導致未定義的行為。 編譯器可以基於此進行優化。
因此,您必須關心這一點。
您可以為static
局部變量cached_results
使用std::array<TriVal, arch_max - 1>
並在其初始化程序中設置它。 static 局部變量的初始化保證是線程安全的並且只執行一次(假設 C++11 或更高版本)。
static bool IsSupportedArch(Arch arch)
{
enum class TriVal : int
{
Unknown,
True,
False
};
constexpr int arch_max = static_cast<int>(Arch::MAX);
const int i_arch = static_cast<int>(arch);
// Cache results in-memory:
static const auto cached_results = []{
std::array<TriVal, arch_max-1> cached_results;
// loop here to set the whole array
return cached_results;
}();
return cached_results[i_arch] == TriVal::True;
}
或者,您可以標記變量thread_local
,但在這種情況下,每個線程都會調用一次 CPUID。
如果您真的關心 CPUID 只被調用幾次所需的次數,您可以將數組元素設置為std::atomic
s:
static std::atomic<TriVal> cached_results[arch_max - 1]{};
在這種情況下,可以進行並發寫入,並且正如您所說,您不在乎哪一個最終成為價值。 不過,您可能希望用寬松的 memory 訂單操作替換加載和存儲,因為您不需要默認的seq_cst
保證:
static bool IsSupportedArch(Arch arch)
{
enum class TriVal : int
{
Unknown,
True,
False
};
constexpr int arch_max = static_cast<int>(Arch::MAX);
const int i_arch = static_cast<int>(arch);
// Cache results in-memory:
static std::atomic<TriVal> cached_results[arch_max - 1]{};
auto value = cached_results[i_arch].load(std::memory_order_relaxed);
if (value == TriVal::Unknown)
{
// OK only if CheckCPUID is guaranteed to always
// return the same value
value = CheckCPUID(arch) ? TriVal::True : TriVal::False;
cached_results[i_arch].store(value, std::memory_order_relaxed);
}
return value == TriVal::True;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.