【问题标题】:Static local variable initialisation in multithreaded environment多线程环境中的静态局部变量初始化
【发布时间】:2013-05-20 01:18:54
【问题描述】:

假设有一个函数(可能是成员函数)

SomeType foo()
{
    static SomeType var = generateVar();
    return var;
}

如果foo 将被多个线程同时“首次”调用,var 将如何初始化?

  1. 是否保证generateVar() 在任何情况下都只会被调用一次(当然如果使用的话)?
  2. 是否保证foo 在任何情况下多次调用时都会返回相同的值?
  3. 原始类型和非原始类型的行为是否存在差异?

【问题讨论】:

    标签: c++ multithreading c++11 initialization c++03


    【解决方案1】:

    关于 C++03:

    C++03 标准定义的抽象机不包含关于什么是线程的正式定义,以及如果同时访问一个对象,程序的结果应该是什么。

    没有同步原语、在不同线程中执行的操作排序、数据竞争等概念。因此,根据定义,每个多线程 C++03 程序都包含未定义的行为。

    当然,在实践中,实现确实提供了记录在案的行为,但标准中没有任何内容指定这种行为应该是什么。因此,我会说这取决于您的编译器。

    其余的答案将集中在 C++11,它确实定义了并发操作的语义。

    关于 C++11:

    是否保证generateVar() 在任何情况下都只会被调用一次(当然如果使用的话)?

    不,在任何情况下都不会。

    var的初始化保证是线程安全的,所以generateVar()不会同时进入,但是如果generateVar()抛出异常,或者@987654325的复制构造函数或者移动构造函数@(如果SomeType当然是一个UDT),那么下一次执行流程进入声明时将重新尝试初始化——这意味着generateVar()将被再次调用。

    根据 C++11 标准的第 6.7/4 段关于使用静态存储持续时间初始化块范围变量:

    [...] 如果初始化通过抛出异常退出,则初始化 不完整,所以下次控制进入声明时会再次尝试。如果控制进入 在变量初始化的同时声明,并发执行应等待 初始化完成。如果控制在变量被执行时递归地重新进入声明 初始化,行为未定义。 [...]

    关于你的下一个问题:

    是否保证 foo 在任何场景下多次调用都会返回相同的值?

    如果它能够返回一个值(见上文),那么是的。

    原始类型和非原始类型的行为有区别吗?

    不,没有,除了基本类型没有复制构造函数或移动构造函数这样的东西,所以复制初始化也不会导致抛出异常的风险(当然除非@ 987654328@ 抛出)。

    【讨论】:

    • “下次将尝试初始化”?这是如何实现的? AFAIK 加载模块时会初始化静态变量,并且在进程生命周期内仅发生一次。
    • @Devolus:块范围内的静态变量在执行流程满足其声明时被初始化。我添加了标准的引用
    • @AndyProwl:谢谢你的回答。你能澄清一下 C++98 中会发生什么吗?
    • 感谢您的澄清。
    • @Andrew:C++98 和 C++03 都没有定义“线程”的概念,所以每个多线程程序都有(形式上)未定义的行为。我猜其他一切都是特定于实现的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 2017-05-14
    • 2017-03-16
    • 2018-09-23
    • 1970-01-01
    • 2019-04-23
    相关资源
    最近更新 更多