这是"Int64"代理的原子吗?

Mar*_*ari 6 c# atomicity

我将创建一个必须是原子的"long"(Int64)代理,因此它在并发应用程序中的副本本质上是安全的.我不能使用Int32,因为它的范围太短.

我知道,只要涉及的数据可以适合双字(32位),原子性就应该得到保证.无论操作系统是32位还是64位.

现在,考虑以下"长"代理人......

注意:我省略了几种方法,因为我不需要它们.在这种情况下,我只需要基本转换为/从真正的长.

public struct SafeLong
    : IConvertible
{
    public SafeLong(long value)
    {
        unchecked
        {
            var arg = (ulong)value;
            this._data = new byte[]
            {
                (byte)arg,
                (byte)(arg >> 8),
                (byte)(arg >> 16),
                (byte)(arg >> 24),
                (byte)(arg >> 32),
                (byte)(arg >> 40),
                (byte)(arg >> 48),
                (byte)(arg >> 56),
            };
        }
    }



    private byte[] _data;



    private long Value
    {
        get
        {
            unchecked
            {
                var lo =
                    this._data[0] |
                    this._data[1] << 8 |
                    this._data[2] << 16 |
                    this._data[3] << 24;

                var hi = 
                    this._data[4] |
                    this._data[5] << 8 |
                    this._data[6] << 16 |
                    this._data[7] << 24;

                return (long)((uint)lo | (ulong)(uint)hi << 32);
            }
        }
    }



    public static implicit operator long(SafeLong value)
    {
        return value.Value;  // implicit conversion
    }



    public static explicit operator SafeLong(long value)
    {
        return new SafeLong(value);  // explicit conversion
    }


    #region IConvertible implementation

    public TypeCode GetTypeCode()
    {
        return Type.GetTypeCode(typeof(SafeLong));
    }

    public object ToType(Type conversionType, IFormatProvider provider)
    {
        return Convert.ChangeType(this.Value, conversionType);
    }

    public long ToInt64(IFormatProvider provider)
    {
        return this.Value;
    }

    // ... OMISSIS (not implemented) ...

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

好吧,它看起来像我期望的那样完美.

这是一个小测试:

class Program
{
    static void Main(string[] args)
    {
        var sla = (SafeLong)12345678987654321L;
        var la = (long)sla;
        Console.WriteLine(la);

        var slb = (SafeLong)(-998877665544332211L);
        var lb = (long)slb;
        Console.WriteLine(lb);

        Console.WriteLine(Marshal.SizeOf(typeof(SafeLong)));
        Console.WriteLine(Marshal.SizeOf(sla));
        Console.WriteLine(Marshal.SizeOf(slb));

        long lc = new SafeLong(556677);
        var slc = slb;
        Console.WriteLine(slc);
        slc = (SafeLong)lc;
        Console.WriteLine(slc);
        Console.WriteLine(slb);

        Console.Write("Press any key...");
        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

SizeOf函数总是产生4个字节作为我的代理的大小.这个值是否保证了SafeLong-to-SafeLong副本的原子性,或者4个字节应该被解释为"真正的物理双字"?

无论长< - > SafeLong的非原子性:它都将被包含在安全的上下文中.

非常感谢提前.

Eri*_*ert 13

你是对的,这是原子的,但圣洁的善良对于一个简单的问题来说这是一个复杂的解决方案.如果你想要一个长度值但使用引用为原子的结构,只需填充长!事实上,如果你这样做,那么你可以制作任何结构类型的原子版本,所以让我们这样做:

public struct SafeThing<T> where T : struct
{
    private object boxedThing;

    public SafeThing(T value)
    {
        boxedThing = value;
    }

    public T Value { get { return boxedThing == null ? default(T) : (T)boxedThing; } }

    public static implicit operator T(SafeThing<T> value)
    {
        return value.Value; 
    }

    public static implicit operator SafeThing<T>(T value)
    {
        return new SafeThing(value); 
    }
}
Run Code Online (Sandbox Code Playgroud)

而且你已经完成了.你为什么要用阵列搞乱这一切?

另外,我注意到在您的实现中,您已经向后转换了显式/隐式转换.如果转换无损且不抛出,则只应隐式转换.您从SafeLong到long的隐式转换可以抛出,因此它不应该是隐式的.你从long到SafeLong的显式转换不能抛出并且是无损的,所以如果你想要的话它可能是隐含的.正如你所看到的,我已经通过使两个方向无损且无投掷来解决我的实现中的问题,因此它们都是隐含的.

请注意,此结构本质上是一个围绕盒装值的强类型包装器; 如果在CLR的第一个版本中可以使用泛型类型,毫无疑问,盒装值类型将使用某种类型的类型实现,就像可空值类型类似地通过特殊泛型类型实现一样.

SizeOf函数总是产生4个字节作为我的代理的大小.这个值是否保证了SafeLong-to-SafeLong副本的原子性?

嗯,是的,不.

首先,"SizeOf"方法不会在内存中给出结构的大小; 它在跨越托管/非托管边界持久化时给出结构的大小.这不一定与托管内存中结构的大小相同; 它通常是相同的,但不保证是相同的.如果您想知道托管内存中结构的大小,则需要打开"不安全"模式并使用"sizeof"运算符.

实际上,大小为4的结构的副本总是原子的,只要它被复制到四字节边界上对齐的位置即可.该语言规范并不保证任何四字节结构将被原子复制,但事实上这是我们实现真实的.

在您的特定情况下,这四个字节是对数组的引用; 语言规范保证引用总是原子复制.

  • 为什么要将东西作为结构中的对象,而不是类中的未装箱字段? (3认同)