我正在开发一个需要具有可变语义的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
如您所见,某些迭代有很大的时间差异。花费异常时间的确切迭代略有不同,但至少有一次迭代存在一致问题。
有人可以阐明为什么 volatile 写入(有时)在类成员上比在结构成员上花费的时间更长吗?
顺便说一句,上面的结果是用.Net 4.5和Release build产生的
最佳答案
时间差异很可能是循环内 var volatileLong = new VolatileLongClass();
的结果;该语句会导致编译器一次分配空间来保存 VolatileLongClass
引用,然后在每次通过循环时创建一个新对象并将引用存储到该位置。相比之下,语句 var volatileLong = new VolatileLongStruct();
使编译器——一次——分配空间来容纳一个 VolatileLongStruct
实例,然后在每次传递循环通过将其所有数据归零(使用正常而不是 volatile 写入)来改变该预先存在的实例。
请注意,如果代码需要结构字段具有特定的多线程语义,则此类字段应经常公开,并且结构应被视为一组用胶带粘贴在一起的变量 [例如public struct IntPair {public int V1,V2;} IntPair myPair;
应该被视为创建两个单独的变量 myPair.V1
和 myPair.V2
].由于结构 是 ,事实上,变量的组合用胶带粘在一起,并且由于大小不是 1、2 或 4 字节的结构所呈现的任何其他抽象必然是“泄漏”,尤其是在多线程行为方面,结构最好表现出它本来的样子,而不是伪装成它不是的样子。
关于c# - 类成员与结构成员上的 volatile 写入的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17172687/