简体   繁体   English

确定性一次性写入 static 位置的线程安全?

[英]Thread safety for deterministic one-time write to static location?

I'm checking CPUID flags on x86-64 for instruction set extensions, and caching the results in a static array.我正在检查 x86-64 上的 CPUID 标志以获取指令集扩展,并将结果缓存在 static 数组中。 The function doing this may be called from any of several different threads, so race conditions are possible - but does this matter?执行此操作的 function 可以从多个不同线程中的任何一个调用,因此可能出现竞争条件 - 但这有关系吗? Any thread will produce the same results, so I don't care about conflicting writes, and checking the same flags more than once is a non-issue.任何线程都会产生相同的结果,所以我不关心写入冲突,并且多次检查相同的标志是不成问题的。 My conclusion is that this is safe, but am I missing something?我的结论是这是安全的,但我错过了什么吗?

Here's a stripped-down version of what I'm doing:这是我正在做的精简版:

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;
}

so I don't care about conflicting writes所以我不在乎有冲突的写入

Conflicting writes to non-atomic objects are a data race and a data race always causes undefined behavior.对非原子对象的冲突写入是数据竞争,数据竞争总是会导致未定义的行为。 The compiler may optimize based on that.编译器可以基于此进行优化。

Therefore you must care about this.因此,您必须关心这一点。

You can use a std::array<TriVal, arch_max - 1> for the static local variable cached_results and have it be set in its initializer.您可以为static局部变量cached_results使用std::array<TriVal, arch_max - 1>并在其初始化程序中设置它。 The initialization of static local variables is guaranteed to be thread-safe and executed exactly once (assuming C++11 or later). 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;
}

Alternatively you can mark the variable thread_local , but in that case every thread will call CPUID once.或者,您可以标记变量thread_local ,但在这种情况下,每个线程都会调用一次 CPUID。

If you really care that CPUID is called only as few number of times as required, you can make the array elements std::atomic s:如果您真的关心 CPUID 只被调用几次所需的次数,您可以将数组元素设置为std::atomic s:

static std::atomic<TriVal> cached_results[arch_max - 1]{};

In that case it is ok to have concurrent writes and as you say you don't care about which one ends up being the value.在这种情况下,可以进行并发写入,并且正如您所说,您不在乎哪一个最终成为价值。 You probably want to replace the loads and stores with relaxed memory order operations though, since you don't need the default seq_cst guarantees:不过,您可能希望用宽松的 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