c# - 类成员与结构成员上的 volatile 写入的奇怪行为

标签 c# .net performance struct volatile

我正在开发一个需要具有可变语义的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.V1myPair.V2 ].由于结构 ,事实上,变量的组合用胶带粘在一起,并且由于大小不是 1、2 或 4 字节的结构所呈现的任何其他抽象必然是“泄漏”,尤其是在多线程行为方面,结构最好表现出它本来的样子,而不是伪装成它不是的样子。

关于c# - 类成员与结构成员上的 volatile 写入的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17172687/

相关文章:

c# - 如何进行查询以检索特定数据

c# - 如何根据 Entity Framework 中的上下文验证实体?

c# - 如何使用 BinaryReader 正确读取 16 字节无符号整数

c# - 更快地读取文件并快速获取像素颜色?

c# - Entity Framework 4 Table Per Hierarchy - 如何定义子项的导航属性?

c# - 当用户输入诸如 233,232 之类的数字时,当用户点击逗号左侧的 3 时,如何删除计算器中的逗号

c# - C# "pure"中的 lambda 表达式/委托(delegate)是,还是可以?

c# - 如何在每个目录或子目录中找到包含特定字符串的最新文件?

performance - Android Studio 开启时间过长

c - 如何有效地计算C中字符串的长度?