C#静态字段、非线程、竞争条件
C# static field, non thread, race condition
我想知道在下面的示例中首先初始化的是什么
A.a
或 B.b
class A {
static int a = methodA();
static int methodA() { return 5; }
}
class B {
static int b = methodB();
static int methodB() { return A.a; } // here is the problem: `b` depends on `a`
}
如果a
,那就没问题
methodA
returns 5,存储到a
。然后 methodB
检索 a
和 returns 的值。
但是如果b
是第一个,它从a
获取垃圾值,然后a
被初始化为5。
你是对的,这是语言规范本身的竞争条件。任何一个都会是 运行 第一,你无法控制它。您要么祈祷链接您的代码的特定编译,以及 运行 使您的代码“正确”(从您的角度来看)的 JIT,要么您只是编写正确的、确定性的代码。
摆脱这种情况的一种选择是使用一个静态构造函数按顺序初始化依赖的静态变量。
it gets garbage value from a
顺便说一下,这永远不会发生,该字段将被零初始化或由您的代码初始化。
在查看 c# 语言规范时,我看不出有任何竞争条件。这是 static constructors:
部分的引述
To initialize a new closed class type, first a new set of static fields (Static and instance fields) for that particular closed type is created. Each of the static fields is initialized to its default value (Default values). Next, the static field initializers (Static field initialization) are executed for those static fields. Finally, the static constructor is executed.
上面的代码示例中发生的是“静态字段初始化”。规范列出了在默认值初始化之后和调用任何静态构造函数之前立即发生的情况。
以如下代码为例:
using System;
namespace console1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("begin main()");
var x = B.b;
Console.WriteLine($"x = {x}");
}
}
public class B
{
static B()
{
Console.WriteLine("static B()");
}
public static int b = methodB();
static int methodB()
{
Console.WriteLine($"initial B.b value {b}");
Console.WriteLine("B.methodB()");
return A.a;
}
}
public class A
{
static A()
{
Console.WriteLine("static A()");
}
public static int a = methodA();
static int methodA()
{
Console.WriteLine("A.methodA()");
return 5;
}
}
}
控制台输出为:
begin main()
initial B.b value 0
B.methodB()
A.methodA()
static A()
static B()
x = 5
这证实了规范所说的内容,步骤如下:
- class先访问B,开始对变量b进行静态域初始化
- 在静态字段初始化完成之前,可以读取 B.b 的默认值(0,如@Blindy 所说)。
- 遇到对classA的调用,还没有初始化
- 它暂停 class B 的初始化并开始 class A
的初始化
- 发生变量a的静态字段初始化,将值设置为5(您也可以在静态字段初始化完成之前查看此属性的默认值0,但我相信唯一可以这样做的地方发生在
methodA
之前 return
)
- 然后调用静态构造函数。
- 初始化 class B 恢复
- 调用了class B 中的静态构造函数。
- 最后
B.b
返回 5
的值,作为 desired/expected。
这里似乎没有任何竞争条件的可能性,因为第一次引用带有静态成员的 class,当前发生的任何事情都会立即暂停并且 class已完全初始化。
(至少在 OP 所询问的单线程代码中。我最初的想法是多线程代码也不会引入竞争条件,但我不能肯定地说)
我想知道在下面的示例中首先初始化的是什么
A.a
或 B.b
class A {
static int a = methodA();
static int methodA() { return 5; }
}
class B {
static int b = methodB();
static int methodB() { return A.a; } // here is the problem: `b` depends on `a`
}
如果a
,那就没问题
methodA
returns 5,存储到a
。然后 methodB
检索 a
和 returns 的值。
但是如果b
是第一个,它从a
获取垃圾值,然后a
被初始化为5。
你是对的,这是语言规范本身的竞争条件。任何一个都会是 运行 第一,你无法控制它。您要么祈祷链接您的代码的特定编译,以及 运行 使您的代码“正确”(从您的角度来看)的 JIT,要么您只是编写正确的、确定性的代码。
摆脱这种情况的一种选择是使用一个静态构造函数按顺序初始化依赖的静态变量。
it gets garbage value from
a
顺便说一下,这永远不会发生,该字段将被零初始化或由您的代码初始化。
在查看 c# 语言规范时,我看不出有任何竞争条件。这是 static constructors:
部分的引述To initialize a new closed class type, first a new set of static fields (Static and instance fields) for that particular closed type is created. Each of the static fields is initialized to its default value (Default values). Next, the static field initializers (Static field initialization) are executed for those static fields. Finally, the static constructor is executed.
上面的代码示例中发生的是“静态字段初始化”。规范列出了在默认值初始化之后和调用任何静态构造函数之前立即发生的情况。
以如下代码为例:
using System;
namespace console1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("begin main()");
var x = B.b;
Console.WriteLine($"x = {x}");
}
}
public class B
{
static B()
{
Console.WriteLine("static B()");
}
public static int b = methodB();
static int methodB()
{
Console.WriteLine($"initial B.b value {b}");
Console.WriteLine("B.methodB()");
return A.a;
}
}
public class A
{
static A()
{
Console.WriteLine("static A()");
}
public static int a = methodA();
static int methodA()
{
Console.WriteLine("A.methodA()");
return 5;
}
}
}
控制台输出为:
begin main()
initial B.b value 0
B.methodB()
A.methodA()
static A()
static B()
x = 5
这证实了规范所说的内容,步骤如下:
- class先访问B,开始对变量b进行静态域初始化
- 在静态字段初始化完成之前,可以读取 B.b 的默认值(0,如@Blindy 所说)。
- 遇到对classA的调用,还没有初始化
- 它暂停 class B 的初始化并开始 class A 的初始化
- 发生变量a的静态字段初始化,将值设置为5(您也可以在静态字段初始化完成之前查看此属性的默认值0,但我相信唯一可以这样做的地方发生在
methodA
之前return
) - 然后调用静态构造函数。
- 初始化 class B 恢复
- 调用了class B 中的静态构造函数。
- 最后
B.b
返回5
的值,作为 desired/expected。
这里似乎没有任何竞争条件的可能性,因为第一次引用带有静态成员的 class,当前发生的任何事情都会立即暂停并且 class已完全初始化。
(至少在 OP 所询问的单线程代码中。我最初的想法是多线程代码也不会引入竞争条件,但我不能肯定地说)