簡體   English   中英

確定性一次性寫入 static 位置的線程安全?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM