依赖于初始化的顺序
Relying on order of initialisation
根据c++ 14标准,非静态成员变量是按照它们在类中声明的顺序初始化的。下面的精简代码依赖于该规则来控制线程函数。
class foo
{
foo():
keep_going{true},
my_thread(&foo::go,this)
{}
void go()
{
while(keep_going)
check a std::condition_variable and do some work;
}
bool keep_going;
std::thread my_thread;
}
注意,keep_going
在线程对象之前声明,并且应该在线程进入go函数时设置为true
。这很好,似乎工作正常。
然而,这是多线程代码,它是值得多疑的,所以我有两个问题:
1像这样依赖于初始化顺序安全吗?如果没有处理线程,我的实际对象就没有意义,所以我想在构造函数中设置它。
当代码依赖于相对模糊的东西(如初始化顺序)时,将代码交给他人是否不安全?-
符合标准是安全的
-
非常不安全。很少有人意识到这一点,维护您的头文件的人可能会重新排序成员,造成灾难性的后果。
我可不相信。
虽然按照标准是安全的,但我不会这么做。
轶事:我在Windows操作系统上使用visual studio 2013编写了一个定制的ThreadPool。我声明线程池是全局的。当然,根据标准,全局对象在main返回后被析构。线程池析构器试图join
每个线程,但唉!一个死锁。(你可以在这里阅读这个问题:std::thread::join()挂起,如果在使用VS2012 RC时main()退出后调用)。标准非常清楚地说明,如果一个线程是可接合的,那么连接它是没有问题的,但是正如您所看到的,这并没有完美地实现。
为什么我要告诉你这个无关的问题?因为即使是编译器和平台也有一些bug。微妙的事情可能不会在最初几个相关的支持编译器版本中100%正确地实现。
这就是我不同意这个想法的原因。作为解决方法,我将声明用std::unique_ptr
包装的线程,并在构造函数体中初始化它。这样,它就不可能在keep_going
之前被初始化。
foo():
keep_going{true}
{ my_thread = std::make_unique<std::thread>(&foo::go,this); }
我想重写代码,使代码即使对猴子也很明显。
当类foo
是基类时,可能会出现另一个潜在的问题。线程将在未完全构造的对象上启动。如果派生类的构造函数失败会发生什么?在这种情况下,最好将线程执行从构造函数移到start()
方法。
- lambda 作为接受其他参数的参数的初始化顺序
- 大括号或等于初始值设定项初始化顺序
- 类内初始化与构造函数初始化列表的顺序
- C++ 模板中的静态常量初始化顺序
- 如果在 C++ 构造函数中以错误的顺序初始化对象数据,会发生什么类型的错误
- 视觉C++:在 DLL 加载期间,全局变量初始化顺序是否具有确定性?
- 构造函数中没有参数的对象类成员按什么顺序初始化?
- 销毁 pthread 互斥体和 C++ 中的取消初始化顺序
- 线程局部变量的初始化顺序
- 初始化值是否保证通过其自己的地址反映,而不考虑内存顺序
- 类静态变量初始化顺序
- 具有静态存储持续时间的常量初始化变量的初始化顺序
- 解析 CRTP 初始化顺序
- 初始化相等C++的顺序
- 内联初始化的静态 const 类成员的初始化顺序保证
- 使用constexpr的全局初始化顺序
- 初始化与类类型相同的静态成员(静态初始化顺序问题)
- 结构化绑定的标识符是否按顺序初始化?
- c++中的求值顺序初始化数组
- c++标准和C语言在哪里说的是一样的:编译单元(.cpp文件)中的变量是按照声明的顺序初始化的