'const static' 可重入函数内部的 STL 容器

'const static' STL container inside reentrant function

比方说,这是一个服务于多个线程的函数。他们读取 kHKeys 不受保护,因为从同一内存地址读取不是数据竞争。

但是,在第一次读取时,kHKeys 被构建。有可能是在构造的时候,另一个线程进入了reentrantFunction函数。

是否有必要在释放同时调用 reentrantFunction 的线程之前构造 kHKeys

示例:

int reentrantFunction(const std::wstring& key_parent_c)
{
    // const 'static' means that kHKeys is constructed only once —
    // The 1st the function is run — and put into a shared memory space.
    // Otherwise, kHKeys is local and it must be constructed each time the function is called.
    const static std::map<std::wstring, HKEY> kHKeys{ { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER } ,
        { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE } , { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT } ,
        { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG } , { L"HKEY_CURRENT_USER_LOCAL_SETTINGS", HKEY_CURRENT_USER_LOCAL_SETTINGS } ,
        { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA } , { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT } ,
        { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT } , { L"HKEY_USERS", HKEY_USERS } };

    // Use kHKeys 

在线程开始使用reentrantFunction之前构造kHKeys不是必须的。

正如您在此处看到的:static local variables,因为 C++11 标准保证静态局部变量只会被初始化一次。关于可用于确保在多线程环境中进行单一初始化的锁,有一个具体说明:

If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison.

但是 - 如果您使用需要相对较长初始化的静态变量(在您的示例中不是这种情况),并且您的线程需要根据一些实时要求执行(具有最小延迟),您可以考虑在线程开始 运行.

之前,在单独的初始化阶段进行

根据要求,关于在编译时构建 STL 容器的单独回答:

请注意,某些 STL 容器现在是 constexpr,编译器可能会在编译时构造值并将其放在 .data 部分中。您可以(尝试)通过将其声明为 constinit 来强制执行此操作。

例如,让我们创建一个包含前 5 个素数的数组:

#include <string>
#include <array>

constexpr std::array<int, 5> make_primes() {
    std::array<int, 5> v;
    for (int i = 0, j = 2; i < 5; ++j) {
        for(int k = 2; k < j; ++k) {
            if (j % k == 0) {
                continue;
            }
        }
        v[i++] = j;
    }
    return v;
}

int bla(int i)
{
    static constinit std::array<int, 5> primes = make_primes();
    return primes[i];
}

这导致以下代码:https://godbolt.org/z/a5h6TE9f7

bla(int):
        movsx   rdi, edi
        mov     eax, DWORD PTR bla(int)::primes[0+rdi*4]
        ret
bla(int)::primes:
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6

正如您在编译后的代码中所看到的,没有计算出任何素数。这发生在编译时,结果 std::array 被放置在数据部分(部分不可见)。

因为我使用了constinit 如果在编译时无法计算变量,编译器将失败。如果我使用了 constexpr,编译器将尝试在编译时计算该值,但可能会认为编译时的计算成本很高,并在 运行 时初始化变量。