public IViewComponentResult Invoke(FurnitureType furnitureType, bool free)
{
List<Furniture> furniture = _context.Furniture.Where(x => x.FurnitureType == furnitureType).ToList();
return View(furniture);
}
在VS2017上,此代码陷入循环。
** VS2013对此代码做了什么?
答案 0 :(得分:18)
没有特别的事情发生。根据C ++标准:
[stmt.dcl] (强调我的意思)
4动态初始化带有静态存储的块范围变量 持续时间或线程存储持续时间是第一次执行 控制通过其声明;这样的变量被认为 初始化完成后初始化。如果 初始化通过抛出异常退出,初始化是 尚未完成,因此下次进入控件时将再次尝试 声明。如果控件同时输入声明 变量正在初始化,并发执行应等待 完成初始化。 如果控件重新输入 在初始化变量时递归声明, 行为未定义。 [示例:
int foo(int i) { static int s = foo(2*i); // recursive call - undefined return i+1; }
— 最终示例]
我胆怯的语句正是您的程序中发生的事情。这也是标准示例显示为未定义的内容。语言规范指出,实现可以执行其认为适当的任何操作。因此,这可能会导致无限循环,也可能不会导致无限循环,具体取决于您的实现用来防止并发重新进入该块的同步原语(初始化必须是线程安全的。)
即使在C ++ 11之前,递归重入的行为也是不确定的。一个实现可以做任何事情来确保一个对象仅被初始化一次,从而可能产生不同的结果。
但是您不能期望任何特定的事情会发生。更不用说不确定的行为总会给小小的鼻恶魔留下空间。
答案 1 :(得分:4)
该行为是不确定的。它在Visual Studio 2013中“起作用”的原因是它没有实现函数静态函数的线程安全初始化。可能发生的情况是,对GetA()
的第一次调用创建了a
并调用了构造函数。然后,对GetA()
的第二次调用仅返回部分构造的a
。由于构造函数的主体不会初始化,因此调用Print()
的任何操作都不会崩溃。
Visual Studio 2017确实实现了线程安全初始化。并且如果GetA()
尚未初始化,则可能在进入a
时锁定了一些互斥锁,因此对GetA()
的第二次调用会遇到锁定的互斥锁和死锁。
请注意,在这两种情况下,这只是我从观察到的行为中得出的猜测,实际行为是不确定的,例如,GetA()
可能最终会创建两个A
实例。