C#联锁交换

我有一些看起来像这样的游戏:

public static float Time;

float someValue = 123;
Interlocked.Exchange(ref Time, someValue);

我想改变时间是一个Uint32; 但是,当我尝试使用UInt32而不是float作为值时,它会抗议该类型必须是引用类型。 Float不是一个引用类型,所以我知道在非引用类型中做到这一点在技术上是可行的。 有什么实际的方法来使这个UInt32工作?


虽然丑陋,但实际上可以使用unsafe C#代码对枚举或其他可位值为64位或更小的值类型执行原子ExchangeCompareExchange

enum MyEnum { A, B, C };

MyEnum m_e = MyEnum.B;

unsafe void example()
{
    MyEnum e = m_e;
    fixed (MyEnum* ps = &m_e)
        if (Interlocked.CompareExchange(ref *(int*)ps, (int)(e | MyEnum.C), (int)e) == (int)e)
        {
            /// change accepted, m_e == B | C
        }
        else
        {
            /// change rejected
        }
}

违反直觉的部分是解引用指针上的ref表达确实贯穿到枚举的地址。 我认为编译器应该有权在堆栈上生成一个不可见的临时变量,在这种情况下,这是行不通的。 使用风险自负。

[编辑:对于OP所要求的特定类型]

static unsafe uint CompareExchange(ref uint target, uint v, uint cmp)
{
    fixed (uint* p = &target)
        return (uint)Interlocked.CompareExchange(ref *(int*)p, (int)v, (int)cmp);
}

[编辑:和64位无符号长]

static unsafe ulong CompareExchange(ref ulong target, ulong v, ulong cmp)
{
    fixed (ulong* p = &target)
        return (ulong)Interlocked.CompareExchange(ref *(long*)p, (long)v, (long)cmp);
}

(我也尝试使用未公开的C#关键字__makeref来实现这个目的,但是这样做不起作用,因为你不能在dreferenced __refvalue上使用ref ,这太糟糕了,因为CLR将InterlockedExchange函数映射到一个专用的内部函数on TypedReference [由JIT拦截提出的评论,见下文])


[编辑:2017年4月]我最近了解到,当.NET运行在32位模式下(或者,在WOW子系统中)时,64位Interlocked操作无法保证在 Interlocked是原子的,“外部“视图相同的内存位置。 在32位模式下,原子保证仅适用于使用Interlocked (也许是Volatile.*Thread.Volatile* ,TBD?)函数的QWORD访问中的Thread.Volatile*变量。

换句话说,要在32位模式下获得64位原子操作, 所有对QWORD位置的访问必须通过Interlocked进行以保证保证,并且假设(例如)直接读取受到保护,您就不会变得可爱因为您总是使用Interlocked功能进行书写。

最后,请注意CLR中的Interlocked函数被.NET JIT编译器特别识别并受到特殊处理。 看到这里和这里这个事实可能有助于解释我前面提到的反直觉。


Interlocked.Exchange专门用于float (以及其他用于doubleintlongIntPtrobject )的重载。 对于uint没有一个,所以编译器认为最接近的匹配是通用的Interlocked.Exchange<T> - 但在这种情况下, T必须是引用类型。 uint不是引用类型,因此也不起作用 - 因此是错误消息。

换一种说法:

  • 你当前的代码工作,因为它调用Interlocked.Exchange(ref float, float)
  • 将它更改为uint失败,因为没有适用的超载。 确切的错误信息是由编译器猜测你的意思是Interlocked.Exchange<T>(ref T, T)
  • 至于做什么,选项是以下任何一种:

  • 正如Marc所言,可能使用int来代替。
  • 如果您需要额外的范围,请考虑使用long
  • 使用uint但不要尝试写无锁代码
  • 虽然很显然Exchange可以很好地处理某些特定的值类型,但是Microsoft并没有为所有的基本类型实现它。 我无法想象要做到这一点很困难(毕竟他们只是一点点),但可能他们想要保持超负荷计数。


    也许使用int而不是uint ; int有重载。 你需要额外的范围? 如果是这样,尽可能晚地施放/转换。

    链接地址: http://www.djcxy.com/p/44471.html

    上一篇: C# Interlocked Exchange

    下一篇: Relative path to unmanaged DLL