全局变量构造函数/析构函数是否需要线程保护?

时间:2013-08-10 21:26:07

标签: c++ multithreading thread-safety

如果我有一个类,其唯一目的是拥有全局static实例(以确保其构造函数中的代码在main之前运行)并且它使用类static变量,是否需要通过互斥锁保护对此变量的访问?

一个例子会有所帮助:

class WinSock
{
public:
  WinSock()
  {
    if(!(inst++))
      //winsock init
  }
  ~WinSock()
  {
    if(!--inst)
      //winsock deactivate
  }
private:
  static int inst = 0;
}
static WinSock unusedWinSockVar;

这都在使用winsock的任何文件包含的标题中。是否需要保护对inst的访问权限,或者是否无法从多个线程运行此代码,因为线程只会在main运行并在main返回之前销毁时创建一次?< / p>

2 个答案:

答案 0 :(得分:4)

首先,我不认为private: static int inst = 0;是一个有效的构造,我的编译器大声抱怨 - 如果你在项目的某个.cpp文件中省略了类似int WinSock::inst = 0的内容,为简单起见,那没关系。如果没有,并且您的项目完全编译,则所有翻译单元很可能会使用不同的变量,从而导致不正确的行为。

其次,如果任何静态对象构造函数创建了一个新线程,那么您需要使代码线程安全。来自C ++标准p。 3.6.2:

  

如果程序启动一个线程(30.3),则后续初始化   关于a的初始化,变量未被排序   变量在不同的翻译单元中定义。否则,   关于变量的初始化是不确定地排序的   初始化在不同翻译中定义的变量   单元。

不确定排序意味着初始化不会有任何特定的排序,但它不会重叠,因此您不需要任何额外的安全措施。没有排序意味着不同编译unis中的构造函数可能重叠,因此需要线程安全。

第三,你甚至需要这样做吗?你有其他在构造函数中使用winsock的静态对象吗?我真的想不出任何其他理由这样做。

答案 1 :(得分:0)

鉴于您描述的具体方案,没有添加同步就可以了。

您关注的是Winsock在main运行之前(之后)被初始化(并且已经去初始化),但事实确实如此。保证代码也只从一个线程调用一次。这(事实上只有一个线程)使同步变得毫无用处。

假设其他静态全局对象使用Winsock(无论它们是否产生线程),那当然是不安全的,但是使用互斥锁也不会更安全。初始化发生在main之前的实现定义时间点 因此,任何静态全局对象都不能使用此构造以安全,明确定义的方式使用Winsock,因为您不知道是否首先发生了初始化。同步它不会改变那个细节。

注意:不允许在类声明中初始化inst