它是原子的吗?代理
本文关键字:代理 原子的 | 更新日期: 2023-09-27 18:02:35
我将创建一个"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
}
嗯,好像和我预期的一样,运行得很好。
这里有一个小测试:
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();
}
}
SizeOf函数总是产生4字节作为代理的大小。这个值是否保证了saflong -to- safelong副本的原子性,或者这4个字节应该被解释为"真正的物理双字"?
无论长<--> SafeLong:是否具有非原子性,它都将被包含在一个安全的上下文中。
你是对的,这是原子的,但是上帝啊,这是一个简单问题的复杂解决方案。如果您想要一个具有long值但使用原子引用的结构体,只需将long框起来!事实上,如果你这样做,你就可以创建任何结构类型的原子版本,所以让我们这样做:
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);
}
}
你完成了。你为什么要在数组上瞎折腾?
另外,我注意到,在您的实现中,您已经向后获得了显式/隐式转换。只有当转换是无损且不抛出时,转换才应该是隐式的。从SafeLong到long的隐式转换可能会抛出错误,所以它不应该是隐式的。从long到SafeLong的显式转换不会抛出错误,并且是无损的,因此如果您希望它是隐式的,它可以是隐式的。正如你所看到的,我已经在我的实现中解决了这个问题,使两个方向都是无损的和非抛出的,所以它们都是隐式的。
注意,这个结构本质上是对盒装值的强类型包装;如果泛型类型在CLR的第一个版本中可用,那么毫无疑问,盒装值类型将使用类似的某种类型来实现,就像可空值类型类似地由特殊的泛型类型实现一样。
SizeOf函数总是产生4字节作为代理的大小。这个值是否保证副本从saflong到safelong的原子性?
嗯,是也不是。
首先,SizeOf方法没有给出结构体在内存中的大小;它给出了结构体跨托管/非托管边界持久化时的大小。这与托管内存中结构体的大小不一定相同;它通常是相同的,但是不能保证是相同的。如果你想知道托管内存中结构体的大小,那么你需要打开"不安全"模式并使用"sizeof"操作符。
作为一个实际问题,一个大小为4的结构体的副本总是原子的,只要它被复制到一个在4字节边界上对齐的位置。语言规范不保证任何四字节结构都将被自动复制,尽管实际上在我们的实现中是如此。
在你的特殊情况下,这四个字节是对数组的引用;语言规范保证引用总是被自动复制。