首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Interlocked.Exchange可空十进制

Interlocked.Exchange可空十进制
EN

Stack Overflow用户
提问于 2011-04-19 16:43:57
回答 2查看 1.9K关注 0票数 2

我想交换两个可为空的十进制值,如下所示:

代码语言:javascript
复制
o2 = Interlocked.Exchange(ref o1, o2);

类型为‘十进制?’必须是引用类型,才能将其用作泛型类型或方法“System.Threading.Interlocked.Exchange(ref,T )”中的参数“T”。

还有比这更好的主意吗?

代码语言:javascript
复制
decimal? temp = o1;
o1 = o2;
o2 = temp;

提前感谢!

EN

回答 2

Stack Overflow用户

发布于 2011-04-19 16:50:48

有两个想法:

consumer

  • create a Box<T> where T:struct (并使其不可变)将其视为object,并交换一些Box<decimal>引用(

F 211)

在这两种情况下,使用者都应该先复制一个值,然后再进行其他操作(不能重复读取,它可能会在读取之间发生变化)。

票数 7
EN

Stack Overflow用户

发布于 2015-02-25 18:05:15

Interlocked.Exchange试图在运行的任何平台上使用CPU的原子指令。它们在CPU级别是原子的,不需要锁定。这些指令通常只在平台字(通常是32或64位内存)上工作。

可以对堆中的单个单词(如intbyte或对object的引用)进行原子操作。不能放在一个单词中的东西,比如struct (比如Nullable<decimal> ),或者只是一个普通的decimal (简单的decimal),都不能用原子的方式来交换。

解决方法是交换引用您的decimal的对象(如果它是非空的)或者只是一个空值(如果它是空的)。这个object是自动使用一个称为装箱和取消装箱的进程为您创建的。

代码语言:javascript
复制
public volatile object myNullableDecimal = null;

然后在您的代码中您可以:

代码语言:javascript
复制
decimal? newValue = 34.3m;
decimal? oldvalue = (decimal?)Interlocked.Exchange(ref myNullableDecimal, newValue);

您必须显式地将存储在myNullableDecimal中的值转换为decimal?才能使用它们,因为装箱是自动的,但取消装箱不是。

另外,不要将int或任何东西放入Nullable<decimal>decimal中,因为尽管可以隐式地将这些类型转换为Nullable<decimal> (通过隐式转换到decimal),但装箱T:struct只能转换为底层T

代码语言:javascript
复制
object myObj = 23; // will work but myObj is now a boxed int, not a boxed decimal
var myDecimal = (decimal?) myObj; // throws an exception! Can't convert boxed ints to Nullable<decimal>

由于这些复杂的显式强制转换,我建议您使用内置的强制转换方法包装对“对象”的访问。此方法适用于所有可空类型,而不仅仅是小数。如果位置实际上不包含正确的装箱类型,则会引发强制转换异常。不过,要小心:在抛出异常之前,它仍将替换旧值。也就是说,只有当它按预期工作时,它才是原子的。如果它失败了,它可能会在非原子上失败。

代码语言:javascript
复制
public static T? ExchangeNullable<T>(ref object location, T? value) where T:struct
{
    return (T?) Interlocked.Exchange(ref location, value);
}

一个“更安全”的方法只能替换可以转换为正确的返回类型的值,它可能如下所示。此方法是非阻塞的、原子的,如果不能将该值转换为适当的类型,则该方法将永远不会替换旧值。但是,此方法易受饥饿的影响,因为如果线程更改的频率超过验证转换成功所需的时间,则线程可能永远无法更新该值。为了解决这个问题,该方法使用一个可选的CancellationToken来允许通过超时来调用它。避免饥饿问题的唯一方法是使用锁定(实际上是公平的锁定,这比常规锁定还要昂贵)。

只有当您不能保证对象除了适当值类型的装箱类型之外,不会得到其他一些值,这个方法才真正有用。如果您控制对自己代码中的位置的所有访问,这不应该是一个问题,但是由于编译器允许您在任何对象引用上调用这些方法(这可能指向任何东西),更新可能会失败,并且该方法保证它会原子地失败。

代码语言:javascript
复制
public static T? ExchangeNullableSafe<T>(ref object location, T? value, CancellationToken token = default(CancellationToken)) where T : struct
{
    // get the expected value
    var expected = location;
    while (true)
    {
        // check if the cast works
        if (expected is T?)
            {
            // cast works, try the update. This includes a memory barrier so we can just do a normal read to
            // populate the expected value initially.
            var actual = Interlocked.CompareExchange(ref location, value, expected);
            // check if the update worked
            if (actual == expected)
            {
                // update worked. Break out of the loop and return 
                break;
            }
            else
            {
                // cast worked but the value was changed before the update occurred.
                // update the expected value to the one the CompareExchange op gave us and try again.
                // again, the memory barrier in the CompareExchange method guarantees that we are updating the expected value each time we run through the loop
                expected = actual;
            }
        }
        else
        {
            // the cast will fail. Just break out of the loop, try the cast, and let it fail.
            break;
        }
        // since this method is vulnerable to starvation, we allow for cancellation between loops.
        token.ThrowIfCancellationRequested();
    }
    // return the value or throw an exception
    return (T?)expected;
}

现在一切都自动地、原子地、不阻塞地转换。

代码语言:javascript
复制
object myNullableDecimal = null;

// ...

decimal? oldValue;
oldValue = ExchangeNullable<decimal>(ref myNullableDecimal, m4 + 7); // works and is atomic
// oldValue is an empty Nullable<decimal>, myNullableDecimal is a boxed 13m
oldValue = ExchangeNullable<decimal>(ref myNullableDecimal, 7.4m); // also works 
// oldValue is a Nullable<decimal> with value 13m, myNullableDecimal is a boxed 7.4m
var oldValue = ExchangeNullable<decimal>(ref myNullableDecimal, null); // yep, works too
// oldValue is a Nullable<decimal> with value 7.4m, myNullableDecimal is null
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5719770

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档