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

Do global variable constructors/destructors need thread protection?

本文关键字:线程 保护 是否 构造函数 析构函数 全局变量      更新时间:2023-10-16

如果我有一个类的唯一目的是拥有全局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返回之前销毁?

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

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

如果程序启动线程(30.3)变量相对于在不同的翻译单位中定义的变量。否则变量的初始化相对于初始化在不同翻译中定义的变量单元

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

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

考虑到您所描述的特定场景,这在不添加同步的情况下是可以的。

您担心的是Winsock在main运行之前(之后)被初始化(和取消初始化),这是肯定的。代码也保证只能从一个线程调用一次。这(事实上只有一个线程)使得同步变得毫无用处。

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

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