依赖于初始化的顺序

时间:2015-09-30 15:44:31

标签: c++ c++14

根据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依靠这样的初始化顺序是否安全?没有处理线程,我的真实对象没有意义,所以我想在构造函数中设置它。

2当代码依赖于初始化顺序等相对模糊的东西时,给别人提供代码是不安全的吗?

3 个答案:

答案 0 :(得分:5)

  1. 根据标准是安全的。

  2. 非常不安全。很少有人知道这一点,维护你的头文件的人可能会给成员重新排序带来灾难性的后果。

  3. 我不会依赖它。

答案 1 :(得分:4)

虽然标准 是安全的,但我不会这样做。

轶事:我使用visual studio 2013在Windows操作系统上编写了一个自定义的ThreadPool。我宣布线程池是全局的。当然按标准,在main返回后会破坏全局对象。线程池析构函数尝试join每个线程,但唉!死锁。 (你可以在这里阅读这个问题:std::thread::join() hangs if called after main() exits when using VS2012 RC)。该标准非常清楚地表明,如果一个线程是可连接的,加入它就没有问题,但正如你所看到的,这并没有完美实现。

为什么我告诉你这个无关的问题?因为即使编译器和平台也有一些错误。在前几个相关支持的编译器版本中,可能无法100%正确实现细微的内容。

这就是我不愿意接受这个想法的原因。 作为解决方法,我将声明包含在std::unique_ptr中的线程并在构造函数体中初始化它。这样就不可能在keep_going之前初始化它。

foo(): 
  keep_going{true}
   { my_thread = std::make_unique<std::thread>(&foo::go,this); }

答案 2 :(得分:2)

我想重写代码,使代码显而易见,甚至对猴子来说。

当类foo是基类时,可能会出现另一个潜在的问题。线程将在非完全构造的对象上启动。如果派生类的构造函数失败会发生什么?在这种情况下,最好将构造函数中的线程执行移至start()方法。