背景
小明和小強同時簽出了源代碼,如果小強先提交,那么提交成功是合理的,接着小明提交了修改,這時源代碼服務器就會告訴小明有人在他讀取之后做了修改,問他如何處理,源代碼服務器會讓小明把修改合並后再提交。這就是樂觀鎖策略,當然源代碼服務也可以配置為悲觀鎖以避免並行修改。
合理的規避並發修改是企業應用中不能回避的問題,但現實場景是,很多團隊都回避這個問題。今天我介紹一下如何使用離線樂觀鎖處理並發修改。
相關文章:再談在線悲觀鎖、離線悲觀鎖、在線樂觀鎖和離線樂觀鎖。
思路
CAS:Compare And Swap,只有當要修改的值在我讀取后沒有被修改,才會被交換(修改)。
CAS是多線程領域的術語,比如:無鎖的環形隊列就是基於這個實現的。因為CAS的思想和樂觀鎖的思想一致,我就借用一下。
看一下離線樂觀鎖的應用場景:
上圖包含了如下信息:
一、讀取線程A1和修改線程A2是不同的線程,因此才叫離線。例如:表單的讀取數據和修改。
二:CAS的Compare比較的是版本號,Swap的是整條記錄。例如:EntityFramework允許你指定哪些屬性是版本屬性。
代碼示例
設置版本字段
CAS
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 }
運行效果
注意事項
一、示例中我直接將讀取線程讀取的數據,離線修改后傳遞個修改線程了,這樣兩個線程只有一次讀取邏輯,這保證了CAS中Compare的正確性。有些場景你可能需要在修改線程中也進行一次讀取,然后將UI層修改的數據合並過來,這種情況就要注意了,必須要手工指定Compare操作使用第一個線程讀取的版本號,否則會使用第二次讀取的版本號。
二、樂觀離線鎖只適合重來成本很低的場景,否則用戶編輯了兩個小時,你告訴他出現並發問題了,他會瘋的。這種成本很高的操作適合“離線悲觀鎖”。
備注
我就是一個行動跟不上思維的人,因為懶惰,我沒有全面的在項目中采用樂觀鎖,下一個項目一定全面實施。