[英]Interlocked.Exchange nullable decimal
我想交换两个可为空的十进制值,如下所示:
o2 = Interlocked.Exchange(ref o1, o2);
类型为“十进制?” 为了将其用作通用类型或方法“ System.Threading.Interlocked.Exchange(ref T,T)”中的参数“ T”,必须为引用类型。
有没有比这更好的主意:
decimal? temp = o1;
o1 = o2;
o2 = temp;
提前致谢!
两个想法:
object
并投向消费者 Box<T>
类 where T:struct
(并使它不可变),并交换一些Box<decimal>
引用 在这两种情况下,使用者都应先克隆值的值(不要重复读取;读取之间可能会改变)。
Interlocked.Exchange
尝试在您正在运行的任何平台上使用CPU的原子指令。 这些在CPU级别是原子的,不需要锁定。 这些指令通常仅适用于平台字(通常为32位或64位内存)。
可以单个地操作适合单个单词的内容,例如int
, byte
或对堆上object
的引用。 无法将单个单词的内容(例如Nullable<decimal>
类的struct
或仅用于该内容的纯decimal
不能原子交换。
一种解决方法是交换引用decimal
(如果非空)或仅引用空值(如果为空)的对象。 使用称为装箱和拆箱的过程自动为您创建该object
。
public volatile object myNullableDecimal = null;
然后在您的代码中可以执行以下操作:
decimal? newValue = 34.3m;
decimal? oldvalue = (decimal?)Interlocked.Exchange(ref myNullableDecimal, newValue);
您将必须将myNullableDecimal
存储的值显式myNullableDecimal
为decimal?
使用它们,因为装箱是自动的,但装箱不是。
另外,请勿在myNullableDecimal中放入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 null
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.