最近,我的公司已经开始从Visual Studio 2010升级到Visual Studio 2015.我们目前遇到的问题显然似乎源于编译器行为的变化。我们可以构建并运行我们的解决方案,但它似乎陷入僵局(似乎只是空闲:CPU使用率几乎为0)。
逐步调试我们发现了单例对象在初始化期间依赖于自身的问题。这是一个非常简化的版本:
#include <iostream>
using namespace std;
struct Singleton
{
Singleton( int n )
{
cout << "Singleton( " << n << " )" << endl;
cout << Singleton::Instance().mN << endl;
mN = n;
}
static Singleton& Instance()
{
static Singleton instance( 5 );
return instance;
}
int mN;
};
int main() {
cout << Singleton::Instance().mN << endl;
return 0;
}
当然,在我们的代码中还有许多其他事情正在发生,但是这段代码表现出与我们在主项目中看到的相同的行为。在VS2010中,这通常会构建,运行和终止&#34;。在VS2015中它陷入僵局。
我还在ideone.com上尝试了各种版本的C ++,并且所有这些都重现了死锁行为。我觉得这不起作用(也不应该起作用),因为这个对象不应该依赖于它自己。
我更为好奇的是为什么这个&#34;工作&#34;在VS2010?标准对静态变量初始化有什么看法?这只是一个VS2010(可能更早)的编译器错误吗?
答案 0 :(得分:7)
标准说:
如果控件在初始化[具有静态或线程存储持续时间的块作用域变量]的同时进入声明,则并发执行应等待初始化完成。如果控件在初始化变量时以递归方式重新输入声明,则行为未定义。
([stmt.dcl] / 4)
在C ++ 11中所做的更改是本地静态变量的初始化需要是线程安全的。标准不允许在初始化期间再次通过声明的递归,并且结果的UB在你的情况下表现为死锁 - 这很有道理,因为第二次通过声明是永远等待第一个完成。
现在,这也是C ++ 03中未定义的行为,但在C ++ 03实现中,初始化不需要是线程安全的,所以可能发生的是:第一次通过声明,设置一个标志,然后调用构造函数;第二遍看到标志,假定变量已经初始化,然后返回对它的引用。然后初始化完成。
显然,您应该重写代码以避免这种递归初始化。