'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
,编译器将尝试在编译时计算该值,但可能会认为编译时的计算成本很高,并在 运行 时初始化变量。
比方说,这是一个服务于多个线程的函数。他们读取 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
,编译器将尝试在编译时计算该值,但可能会认为编译时的计算成本很高,并在 运行 时初始化变量。