4

我正在开发一个需要long具有可变语义的类型的组件。
由于volatile long.NET 中没有,我创建了一个简单的包装器类型,它使用类处理读/写访问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 构建产生的

4

1 回答 1

1

时间差很可能是var volatileLong = new VolatileLongClass();循环内的结果;该语句导致编译器——一次——分配空间来保存一个VolatileLongClass引用,然后在每次通过循环时创建一个新对象并将引用存储到该位置。相比之下,该语句var volatileLong = new VolatileLongStruct();导致编译器 - 一次 - 分配空间来保存一个VolatileLongStruct实例,然后在每次通过循环时通过将其所有数据清零来改变该预先存在的实例(使用正常,而不是 volatile,写入)。

请注意,如果代码要求结构字段具有特定的多线程语义,则此类字段通常应公开,并且结构应被视为一组用胶带粘在一起的变量[例如public struct IntPair {public int V1,V2;} IntPair myPair;,应视为创建两个独立的变量myPair.V1myPair.V2]。由于 struct实际上用胶带粘在一起的变量的组合,并且由于大小不是 1、2 或 4 字节的 struct 呈现的任何其他抽象都必然是“泄漏的”,尤其是关于多线程行为,结构体最好将自己呈现为它的样子,而不是假装它不是。

于 2013-06-18T17:24:45.017 回答