1

如果我有一个类,其唯一目的是拥有全局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

4

2 回答 2

4

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

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

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

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

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

于 2013-08-10T21:49:09.433 回答
0

鉴于您描述的特定场景,无需添加同步就可以了。

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

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

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

于 2013-08-10T22:10:51.453 回答