我正在开发一个需要long
类型且具有易失性语义的组件
由于.NET中没有volatile long
,我创建了一个简单的包装器类型,它使用Volatile
类来处理读/写访问。
我不确定是否应该使用类或结构,所以我决定对它们进行测试,然后我遇到了一个非常奇怪的行为。
这是测试代码:
internal class Program
{
private class VolatileLongClass
{
private long value;
public long Value
{
get { return Volatile.Read(ref value); }
set { Volatile.Write(ref this.value, value); }
}
}
private struct VolatileLongStruct
{
private long value;
public long Value
{
get { return Volatile.Read(ref value); }
set { Volatile.Write(ref this.value, value); }
}
}
private static void Main()
{
const int iterations = 10;
var totalTime = 0L;
for (var i = 0; i < iterations; i++)
{
var watch = Stopwatch.StartNew();
var volatileLong = new VolatileLongClass(); //<-- change to VolatileLongStruct
for (var j = 0L; j < 10 * 1000 * 1000; j++)
volatileLong.Value = j;
var msElapsed = watch.ElapsedMilliseconds;
Console.Out.WriteLine("Ms Elapsed = {0}", msElapsed);
totalTime += msElapsed;
}
Console.Out.WriteLine("Avg = {0:N2}", (double) totalTime / iterations);
}
}
我为VolatileLongStruct获得的输出:
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Ms Elapsed = 109
Avg = 109.00
struct的上述输出是一致的。但是,VolatileLongClass的输出是:
Ms Elapsed = 17558 <-- ***
Ms Elapsed = 105
Ms Elapsed = 105
Ms Elapsed = 105
Ms Elapsed = 105
Ms Elapsed = 105
Ms Elapsed = 17541 <-- ***
Ms Elapsed = 105
Ms Elapsed = 105
Ms Elapsed = 105
Avg = 3,593.90
正如您所看到的,某些迭代存在很大的时间差异。花费异常时间的精确迭代略有不同,但至少有一次迭代存在一致的问题。
有人可以解释为什么一个易变的写入(有时)在类成员上(有时)长于结构成员吗?
顺便说一下,上面的结果是用.Net 4.5和Release build
生成的答案 0 :(得分:1)
时差很可能是循环中var volatileLong = new VolatileLongClass();
的结果;该语句使编译器 - 一次 - 分配空间来保存VolatileLongClass
引用,然后在每次通过循环时创建一个新对象并将引用存储到该位置。相比之下,语句var volatileLong = new VolatileLongStruct();
导致编译器 - 一次 - 分配空间来保存VolatileLongStruct
实例,然后在每次传递循环时通过将所有实例清零来预先存在实例数据(使用正常而非易失性写入)。
请注意,如果代码要求结构字段具有特定的多线程语义,那么这些字段通常应该公开,结构应该被视为与管道磁带粘在一起的一组变量[例如public struct IntPair {public int V1,V2;} IntPair myPair;
应被视为创建两个单独的变量myPair.V1
和myPair.V2
]。实际上,由于结构是,所以变量的组合与管道磁带粘在一起,并且由于大小不是1,2或4个字节的结构所呈现的任何其他抽象必然是“漏洞”,特别是关于多线程行为,结构更好地表现为它本身,而不是伪装成它不是。