我有几个C++源文件,其中一些数据/回调在头文件的映射中注册。
// foo.h
class Foo final {
public:
static void Register(...) { ... }
private:
inline static std::map<...> g_map{};
};
// a.cc, b.cc, etc.
#include "foo.h"
namespace {
struct Init {
Init() {
Foo::Register(...);
}
};
Init g_init{}; // the inversion-of-control trick
}那么有必要使用锁来保护g_map吗?我的直觉是_dl_init (在main之前的运行时调用)的执行是单线程的,尽管顺序是a.cc,b.cc,...是未知的,我说的对吗?
发布于 2020-02-20 18:39:53
如果在main启动之前初始化非局部静态变量,则它们将在basic.start.main中定义的线程中初始化
执行程序的
启动执行的主线程(4.7,33.3),在该线程中调用主函数,并且可以初始化(6.6.2)和销毁(6.6.4)静态存储持续时间的变量。
但是静态变量的C++初始化并不是那么简单:-(。同样的草案在后面的basic.start.dynamic§4中说(强调我的):
它是由实现定义的,在main or的第一条语句之前,是否对具有静态存储持续时间的非本地非内联变量的动态初始化进行排序。如果它被延迟,它会强烈地发生在任何非初始化odr之前-使用在与要初始化的变量相同的转换单元中定义的任何非内联函数或非内联变量。它是由实现定义的,在程序中的哪些线程和哪些点上,这种延迟的动态初始化发生在。
我的理解是,该标准最大限度地防止程序员在初始化静态变量时做出假设。如果实现是程序员友好的,它将在单个线程中初始化main之前的所有非局部静态变量-标准允许这样做。但不幸的是,它将是不可移植的,因为标准没有强制要求它。
TL/DR:如果你的目标是一个单一的实现,它保证在调用main之前初始化非局部静态变量,那么你可以确定这个初始化将在一个线程中发生。如果你是勇敢的,你可以试着理解在任何非初始化odr之前它会发生什么-使用任何非内联函数或非内联变量,定义在与要初始化的变量相同的转换单元中,以及如何使用它。如果你只想要一个可移植的和健壮的代码,接受这样一个事实,即龙隐藏在静态初始化附近,并且它可能发生在任何线程中……
https://stackoverflow.com/questions/60316014
复制相似问题