‘const static’STL容器初始化(可重入函数内部)

问题描述 投票:0回答:2

假设这是一个服务于多个线程的函数。他们读取不受保护的

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 
c++ multithreading constructor static stl
2个回答
4
投票

在线程开始使用

kHKeys
之前,不必构造
reentrantFunction

正如您在这里看到的:静态局部变量,从 C++11 开始,它标准保证静态局部变量只会被初始化一次。有一个关于锁的具体注释,可用于确保多线程环境中的单一初始化:

如果多个线程尝试初始化同一个静态局部变量 变量同时发生,初始化只发生一次(类似 可以使用 std::call_once 获得任意函数的行为。
注意:此功能的通常实现使用以下变体 双重检查锁定模式,减少了运行时开销 已初始化的局部静态为单个非原子布尔值 比较。

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


2
投票

根据要求提供有关在编译时构建 STL 容器的单独答案:

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

作为示例,让我们创建前 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
,编译器会尝试在编译时计算该值,但可能会认为计算在编译时过于昂贵,并在运行时初始化变量。

© www.soinside.com 2019 - 2024. All rights reserved.