天天看點

.NET:防止并發修改 之 離線樂觀鎖代碼示例

背景

小明和小強同時簽出了源代碼,如果小強先送出,那麼送出成功是合理的,接着小明送出了修改,這時源代碼伺服器就會告訴小明有人在他讀取之後做了修改,問他如何處理,源代碼伺服器會讓小明把修改合并後再送出。這就是樂觀鎖政策,當然源代碼服務也可以配置為悲觀鎖以避免并行修改。

合理的規避并發修改是企業應用中不能回避的問題,但現實場景是,很多團隊都回避這個問題。今天我介紹一下如何使用離線樂觀鎖處理并發修改。

相關文章:再談線上悲觀鎖、離線悲觀鎖、線上樂觀鎖和離線樂觀鎖。

思路

CAS:Compare And Swap,隻有當要修改的值在我讀取後沒有被修改,才會被交換(修改)。

CAS是多線程領域的術語,比如:無鎖的環形隊列就是基于這個實作的。因為CAS的思想和樂觀鎖的思想一緻,我就借用一下。

看一下離線樂觀鎖的應用場景:

.NET:防止并發修改 之 離線樂觀鎖代碼示例

上圖包含了如下資訊:

一、讀取線程A1和修改線程A2是不同的線程,是以才叫離線。例如:表單的讀取資料和修改。

二:CAS的Compare比較的是版本号,Swap的是整條記錄。例如:EntityFramework允許你指定哪些屬性是版本屬性。

代碼示例

設定版本字段

.NET:防止并發修改 之 離線樂觀鎖代碼示例

CAS

.NET:防止并發修改 之 離線樂觀鎖代碼示例
1         /// <summary>
 2         /// 執行修改。
 3         /// </summary>
 4         public void Handle(TCommand command)
 5         {
 6             var unitOfWork = ServiceLocator.Current.GetInstance<TUnitOfWork>();
 7 
 8             unitOfWork
 9                 .GetRepository<TRepository>()
10                 .MarkAsModified(command.Aggregate);
11 
12             unitOfWork.Commit();
13         }      
.NET:防止并發修改 之 離線樂觀鎖代碼示例

運作效果

.NET:防止并發修改 之 離線樂觀鎖代碼示例

注意事項

一、示例中我直接将讀取線程讀取的資料,離線修改後傳遞個修改線程了,這樣兩個線程隻有一次讀取邏輯,這保證了CAS中Compare的正确性。有些場景你可能需要在修改線程中也進行一次讀取,然後将UI層修改的資料合并過來,這種情況就要注意了,必須要手工指定Compare操作使用第一個線程讀取的版本号,否則會使用第二次讀取的版本号。

二、樂觀離線鎖隻适合重來成本很低的場景,否則使用者編輯了兩個小時,你告訴他出現并發問題了,他會瘋的。這種成本很高的操作适合“離線悲觀鎖”。

備注

我就是一個行動跟不上思維的人,因為懶惰,我沒有全面的在項目中采用樂觀鎖,下一個項目一定全面實施。