假设有一个函数(可能是成员函数)
SomeType foo()
{
static SomeType var = generateVar();
return var;
}
如果同时从多个线程“第一次”调用,将如何var
初始化?foo
- 是否保证
generateVar()
在任何情况下都只会调用一次(当然如果使用)? - 是否保证
foo
在任何情况下多次调用时都会返回相同的值? - 原始类型或非原始类型的行为是否存在差异?
假设有一个函数(可能是成员函数)
SomeType foo()
{
static SomeType var = generateVar();
return var;
}
如果同时从多个线程“第一次”调用,将如何var
初始化?foo
generateVar()
在任何情况下都只会调用一次(当然如果使用)?foo
在任何情况下多次调用时都会返回相同的值?关于 C++03:
C++03 标准定义的抽象机不包含线程是什么的正式定义,以及如果同时访问一个对象,程序的结果应该是什么。
没有同步原语、在不同线程中执行的操作排序、数据竞争等的概念。因此,根据定义,每个多线程 C++03 程序都包含未定义的行为。
当然,在实践中,实现确实提供了记录在案的行为,但标准中没有任何内容指定这种行为应该是什么。因此,我会说这取决于您的编译器。
其余的答案将集中在 C++11,它确实定义了并发操作的语义。
关于 C++11:
是否保证
generateVar()
在任何情况下都只会调用一次(当然如果使用)?
不,在任何情况下都不会。
的初始化var
保证是线程安全的,所以generateVar()
不会同时进入,但是如果抛出异常generateVar()
,或者是的拷贝构造函数或者移动构造函数SomeType
(SomeType
当然是UDT的话),那么初始化将是下次执行流程进入声明时重新尝试 - 这意味着generateVar()
将再次被调用。
根据 C++11 标准的第 6.7/4 段,关于使用静态存储持续时间初始化块范围变量:
[...]如果初始化通过抛出异常退出,则初始化未完成,因此将在下次控制进入声明时再次尝试。如果在变量初始化时控制同时进入声明,则并发执行将等待初始化完成。如果在初始化变量时控件以递归方式重新进入声明,则行为未定义。[...]
关于你的下一个问题:
是否保证 foo 在任何情况下多次调用时都会返回相同的值?
如果它将设法返回一个值(见上文),那么是的。
原始类型或非原始类型的行为是否存在差异?
不,没有,除了原始类型没有复制构造函数或移动构造函数之类的东西,因此复制初始化也不会导致抛出异常(除非当然generateVar()
抛出)。