在重新分配内部具有易失性字段的结构字段时,假设易失性语义是否安全?
Is it safe to assume volatile semantics when reassigning a struct field with a volatile field inside?
看看下面的例子:
struct MyStruct
{
private volatile int _field;
public void Set(int v) => _field = v;
}
class MyClass
{
private MyStruct _myStruct;
// is it same as _myStruct.Set(0)?
public void SomeMethod() => _myStruct = new MyStruct();
}
基本上,问题是重新分配内部具有易失性字段的整个结构是否使用与直接分配字段相同的易失性语义。
volatile
前缀是使用 modreq
编译器属性实现的,只有在直接通过标记为 volatile
的字段进行访问时才由编译器强制执行。
ECMA-335 规范实际上没有任何字段的 volatile
属性,并且 CLR 不强制对此类字段进行易失性访问,因此由编译器发出 volatile.
生成的 IL 中的前缀。仅当您直接访问该字段时才会这样做。
你反编译下面的代码就可以很容易地看到这一点
public class C {
static s s1;
static s s2;
public void M() {
s1 = new s();
s2 = s1;
s2.i = s1.i;
}
}
struct s{
public volatile int i;
}
.class public auto ansi beforefieldinit C
extends [System.Private.CoreLib]System.Object
{
// Fields
.field private static valuetype s s1
.field private static valuetype s s2
// Methods
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x2050
// Code size 47 (0x2f)
.maxstack 8
IL_0000: nop
IL_0001: ldsflda valuetype s C::s1
IL_0006: initobj s
IL_000c: ldsfld valuetype s C::s1
IL_0011: stsfld valuetype s C::s2
IL_0016: ldsflda valuetype s C::s2
IL_001b: ldsflda valuetype s C::s1
IL_0020: volatile.
IL_0022: ldfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_0027: volatile.
IL_0029: stfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_002e: ret
} // end of method C::M
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2080
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
} // end of class C
.class private sequential ansi sealed beforefieldinit s
extends [System.Private.CoreLib]System.ValueType
{
// Fields
.field public int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) i
} // end of class s
看看下面的例子:
struct MyStruct
{
private volatile int _field;
public void Set(int v) => _field = v;
}
class MyClass
{
private MyStruct _myStruct;
// is it same as _myStruct.Set(0)?
public void SomeMethod() => _myStruct = new MyStruct();
}
基本上,问题是重新分配内部具有易失性字段的整个结构是否使用与直接分配字段相同的易失性语义。
volatile
前缀是使用 modreq
编译器属性实现的,只有在直接通过标记为 volatile
的字段进行访问时才由编译器强制执行。
ECMA-335 规范实际上没有任何字段的 volatile
属性,并且 CLR 不强制对此类字段进行易失性访问,因此由编译器发出 volatile.
生成的 IL 中的前缀。仅当您直接访问该字段时才会这样做。
你反编译下面的代码就可以很容易地看到这一点
public class C {
static s s1;
static s s2;
public void M() {
s1 = new s();
s2 = s1;
s2.i = s1.i;
}
}
struct s{
public volatile int i;
}
.class public auto ansi beforefieldinit C
extends [System.Private.CoreLib]System.Object
{
// Fields
.field private static valuetype s s1
.field private static valuetype s s2
// Methods
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x2050
// Code size 47 (0x2f)
.maxstack 8
IL_0000: nop
IL_0001: ldsflda valuetype s C::s1
IL_0006: initobj s
IL_000c: ldsfld valuetype s C::s1
IL_0011: stsfld valuetype s C::s2
IL_0016: ldsflda valuetype s C::s2
IL_001b: ldsflda valuetype s C::s1
IL_0020: volatile.
IL_0022: ldfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_0027: volatile.
IL_0029: stfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_002e: ret
} // end of method C::M
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2080
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
} // end of class C
.class private sequential ansi sealed beforefieldinit s
extends [System.Private.CoreLib]System.ValueType
{
// Fields
.field public int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) i
} // end of class s