我有一个用互斥体保护的集合。初始化后,它只能被读取,所以我在那里不需要互斥体。
该集合在全局静态初始值设定项中进行初始化和填充。我知道全局静态初始化是在单个翻译单元内保证的。是否可以保证全局静态初始化是单线程的?
我有一个受 Schwarz 计数器保护的静态集合,并由其他静态对象的构造函数填充。该容器与互斥锁相关联。鉴于该集合在启动后是只读的main,如果我可以保证在单个线程中调用静态构造函数,我想摆脱互斥体。
我的理解是,静态初始化顺序通常在单个翻译单元内明确定义,但在翻译单元之间未指定。标准是否允许静态对象由不同的运行时提供的线程初始化/构造?
施瓦茨计数器:
头文件:
struct Init
{
Init();
~Init();
};
namespace
{
Init _init;
}
extern std::map<int, std::unique_ptr<...>> &protected;
Run Code Online (Sandbox Code Playgroud)
源文件:
namespace
{
int init_count;
std::aligned_storage<sizeof(std::map<int, std::unique_ptr<...>>), alignof(std::map<int, std::unique_ptr<...>>>)> protected_storage;
}
std::map<int, std::uniqe_ptr<...>> &protected = *reinterpret_cast<std::map<int, std::unique_ptr<...>> *>(&protected_storage);
Init::Init()
{
if (!init_counter++)
{
new(&protected_storage) std::map<int, std::unique_ptr<...>>();
}
}
Init::~Init()
{
if (!--init_counter)
{
protected.~std::map<int, std::unique_ptr<...>>();
}
}
Run Code Online (Sandbox Code Playgroud)
征集人群:
struct helper
{
helper(...)
{
protected.insert(std::make_pair(...));
}
};
Run Code Online (Sandbox Code Playgroud)
扩展了一个宏,用于创建助手的静态实例。
是否可以保证全局静态初始化是单线程的?
你的意思是动态初始化。不,明确不保证单线程初始化。
从 3.6.2 开始:
如果程序启动线程 (30.3),则变量的后续初始化相对于不同翻译单元中定义的变量的初始化是无序的。否则,变量的初始化相对于不同翻译单元中定义的变量的初始化是不确定的。如果程序启动线程,则变量的后续无序初始化相对于所有其他动态初始化都是无序的。否则,变量的无序初始化相对于所有其他动态初始化是不确定地排序的
因此,如果您在程序中启动一个线程,那么理论上,来自两个不同 TU 的两个不同全局变量的构造函数可以同时从两个不同线程运行。
处理这些问题的最佳方法是将静态存储持续时间变量包装为以下“单例模式”中的局部静态变量:
const T& f()
{
static T t(a,b,c);
return t;
}
Run Code Online (Sandbox Code Playgroud)
最新的标准保证 的构造t是线程安全的,因此您根本不需要互斥体(至少不需要明确指定,编译器将为您生成防护)。
作为一个额外的好处,该对象是在第一次调用时“惰性”构造的f,因此您无需担心初始化顺序。如果多个这样的单例在其构造函数中相互调用(当然,前提是依赖关系是非循环的),它们将以工作顺序进行初始化。非局部变量的情况并非如此。
| 归档时间: |
|
| 查看次数: |
2988 次 |
| 最近记录: |