[英]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.