静态局部变量中的竞争条件

tin*_*yaa 4 c++ singleton multithreading initialization race-condition

我目前正在阅读Effective C++.有一个关于使用静态局部变量的部分,它说如果多个线程访问一个静态变量,那么在该变量的初始化期间可能存在竞争条件.

至少这是我的解释.这是真的?例如,在C#中,类静态变量的初始化永远不会有竞争条件.

例如,此代码在静态变量初始化期间是否具有竞争条件?

FileSystem& tfs()
{
    static FileSystem fs;
    return fs;
}
Run Code Online (Sandbox Code Playgroud)

以下是本书的除外.

这是应用于tfs和tempDir的技术:

class FileSystem { ... }; // as before

FileSystem& tfs() // this replaces the tfs object; it could static in the FileSystem class
{
    static FileSystem fs; // define and initialize a local static object
    return fs; // return a reference to it
}
Run Code Online (Sandbox Code Playgroud)

.

class Directory { ... }; // as before

Directory::Directory( params ) // as before, except references to tfs are now to tfs()
{
 ...
 std::size_t disks = tfs().numDisks();
 ...
}

Directory& tempDir() // this replaces the tempDir object; it could be static in the Directory class
{
    static Directory td; // define/initialize local static object
    return td; // return reference to it
}
Run Code Online (Sandbox Code Playgroud)

这个修改过的系统程序的客户端与以前一样,除了它们现在引用tfs()tempDir()不是tfstempDir.也就是说,它们使用函数返回对象的引用,而不是使用对象本身.

此方案规定的引用返回函数总是很简单:在第1行定义和初始化本地静态对象,在第2行返回它.这种简单性使它们成为内联的理想选择,特别是如果它们被频繁调用(参见条款30) ).另一方面,这些函数包含静态对象的事实使它们在多线程系统中成为问题.然后,任何类型的非常量静态对象 - 本地或非本地 - 在存在多个线程时都会等待发生.解决此类问题的一种方法是在程序的单线程启动部分期间手动调用所有引用返回函数.这消除了与初始化相关的竞争条件.

Man*_*rse 10

本节已过时.C++ 03标准没有提到线程,所以当C++实现添加它们时,它们就语言结构的线程安全性做了他们想做的任何事情.一种常见的选择是不确保静态局部变量的线程安全初始化.

在C++ 11中,本地静态变量保证只初始化一次,这是程序控制流第一次通过它们的声明,即使这在多个线程上同时发生6.7/4:

允许实现在静态或线程存储持续时间内执行其他块范围变量的早期初始化,条件是允许实现在命名空间范围内静态初始化具有静态或线程存储持续时间的变量(3.6.2).否则,在第一次控制通过其声明时初始化这样的变量; 这样的变量在初始化完成后被认为是初始化的.如果通过抛出异常退出初始化,则初始化未完成,因此下次控制进入声明时将再次尝试初始化.如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成.

即便如此,这只能确保初始化是安全的.如果您计划同时使用FileSystem从多个线程返回的,则FileSystem必须自己提供线程安全操作.