假设这是一个服务于多个线程的函数。他们读取不受保护的
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
在线程开始使用
kHKeys
之前,不必构造reentrantFunction
。
正如您在这里看到的:静态局部变量,从 C++11 开始,它标准保证静态局部变量只会被初始化一次。有一个关于锁的具体注释,可用于确保多线程环境中的单一初始化:
如果多个线程尝试初始化同一个静态局部变量 变量同时发生,初始化只发生一次(类似 可以使用 std::call_once 获得任意函数的行为。
注意:此功能的通常实现使用以下变体 双重检查锁定模式,减少了运行时开销 已初始化的局部静态为单个非原子布尔值 比较。
但是 - 如果您使用需要相对较长初始化的静态变量(不是您示例中的情况),并且您的线程需要根据一些实时要求执行(具有最小延迟),您可以考虑这样做在单独的初始化阶段,在线程开始运行之前。
根据要求提供有关在编译时构建 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
,编译器会尝试在编译时计算该值,但可能会认为计算在编译时过于昂贵,并在运行时初始化变量。