我想交换两个可为空的十进制值,如下所示:
o2 = Interlocked.Exchange(ref o1, o2);类型为‘十进制?’必须是引用类型,才能将其用作泛型类型或方法“System.Threading.Interlocked.Exchange(ref,T )”中的参数“T”。
还有比这更好的主意吗?
decimal? temp = o1;
o1 = o2;
o2 = temp;提前感谢!
发布于 2011-04-19 16:50:48
有两个想法:
consumer
Box<T> 类 where T:struct (并使其不可变)将其视为object,并交换一些Box<decimal>引用(
F 211)
在这两种情况下,使用者都应该先复制一个值,然后再进行其他操作(不能重复读取,它可能会在读取之间发生变化)。
发布于 2015-02-25 18:05:15
Interlocked.Exchange试图在运行的任何平台上使用CPU的原子指令。它们在CPU级别是原子的,不需要锁定。这些指令通常只在平台字(通常是32或64位内存)上工作。
可以对堆中的单个单词(如int、byte或对object的引用)进行原子操作。不能放在一个单词中的东西,比如struct (比如Nullable<decimal> ),或者只是一个普通的decimal (简单的decimal),都不能用原子的方式来交换。
解决方法是交换引用您的decimal的对象(如果它是非空的)或者只是一个空值(如果它是空的)。这个object是自动使用一个称为装箱和取消装箱的进程为您创建的。
public volatile object myNullableDecimal = null;然后在您的代码中您可以:
decimal? newValue = 34.3m;
decimal? oldvalue = (decimal?)Interlocked.Exchange(ref myNullableDecimal, newValue);您必须显式地将存储在myNullableDecimal中的值转换为decimal?才能使用它们,因为装箱是自动的,但取消装箱不是。
另外,不要将int或任何东西放入Nullable<decimal>或decimal中,因为尽管可以隐式地将这些类型转换为Nullable<decimal> (通过隐式转换到decimal),但装箱T:struct只能转换为底层T。
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>由于这些复杂的显式强制转换,我建议您使用内置的强制转换方法包装对“对象”的访问。此方法适用于所有可空类型,而不仅仅是小数。如果位置实际上不包含正确的装箱类型,则会引发强制转换异常。不过,要小心:在抛出异常之前,它仍将替换旧值。也就是说,只有当它按预期工作时,它才是原子的。如果它失败了,它可能会在非原子上失败。
public static T? ExchangeNullable<T>(ref object location, T? value) where T:struct
{
return (T?) Interlocked.Exchange(ref location, value);
}一个“更安全”的方法只能替换可以转换为正确的返回类型的值,它可能如下所示。此方法是非阻塞的、原子的,如果不能将该值转换为适当的类型,则该方法将永远不会替换旧值。但是,此方法易受饥饿的影响,因为如果线程更改的频率超过验证转换成功所需的时间,则线程可能永远无法更新该值。为了解决这个问题,该方法使用一个可选的CancellationToken来允许通过超时来调用它。避免饥饿问题的唯一方法是使用锁定(实际上是公平的锁定,这比常规锁定还要昂贵)。
只有当您不能保证对象除了适当值类型的装箱类型之外,不会得到其他一些值,这个方法才真正有用。如果您控制对自己代码中的位置的所有访问,这不应该是一个问题,但是由于编译器允许您在任何对象引用上调用这些方法(这可能指向任何东西),更新可能会失败,并且该方法保证它会原子地失败。
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;
}现在一切都自动地、原子地、不阻塞地转换。
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 nullhttps://stackoverflow.com/questions/5719770
复制相似问题