C#使用讀寫鎖解決多線程并發(fā)問題
一、簡介
在開發(fā)程序的過程中,難免少不了寫入錯誤日志這個關鍵功能。實現(xiàn)這個功能,可以選擇使用第三方日志插件,也可以選擇使用數(shù)據(jù)庫,還可以自己寫個簡單的方法把錯誤信息記錄到日志文件?,F(xiàn)在我們來講下最后一種方法:
在選擇最后一種方法實現(xiàn)的時候,若對文件操作與線程同步不熟悉,問題就有可能出現(xiàn)了,因為同一個文件并不允許多個線程同時寫入,否則會提示“文件正在由另一進程使用,因此該進程無法訪問此文件”。這是文件的并發(fā)寫入問題,就需要用到線程同步。而微軟也給線程同步提供了一些相關的類可以達到這樣的目的,本文使用到的 System.Threading.ReaderWriterLockSlim 便是其中之一。該類用于管理資源訪問的鎖定狀態(tài),可實現(xiàn)多線程讀取或進行獨占式寫入訪問。利用這個類,我們就可以避免在同一時間段內多線程同時寫入一個文件而導致的并發(fā)寫入問題。讀寫鎖是以 ReaderWriterLockSlim 對象作為鎖管理資源的,不同的 ReaderWriterLockSlim 對象中鎖定同一個文件也會被視為不同的鎖進行管理,這種差異可能會再次導致文件的并發(fā)寫入問題,所以 ReaderWriterLockSlim 應盡量定義為只讀的靜態(tài)對象。
ReaderWriterLockSlim 有幾個關鍵的方法,本文僅討論寫入鎖:
1.調用 EnterWriteLock 方法 進入寫入狀態(tài),在調用線程進入鎖定狀態(tài)之前一直處于阻塞狀態(tài),因此可能永遠都不返回。
2.調用 TryEnterWriteLock 方法 進入寫入狀態(tài),可指定阻塞的間隔時間,如果調用線程在此間隔期間并未進入寫入模式,將返回false。
3.調用 ExitWriteLock 方法 退出寫入狀態(tài),應使用 finally 塊執(zhí)行 ExitWriteLock 方法,從而確保調用方退出寫入模式。
二、不使用讀寫鎖寫入文件:
代碼:
class Program { static int LogCount = 100; static int WritedCount = 0; static int FailedCount = 0; static void Main(string[] args) { //迭代運行寫入日志記錄,由于多個線程同時寫入同一個文件將會導致錯誤 Parallel.For(0, LogCount, e => { WriteLog1(); }); Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString())); Console.Read(); } #region 未加入讀寫鎖 //不使用讀寫鎖寫入文件 static void WriteLog1() { try { var logFilePath = "log.txt"; var now = DateTime.Now; var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString()); File.AppendAllText(logFilePath, logContent); WritedCount++; } catch (Exception ex) { FailedCount++; Console.WriteLine(ex.Message); } } #endregion }
運行結果:
不是所有的log都能寫入到log.txt,因為不適用讀寫錯可能會出現(xiàn)上面提到的:“文件正在由另一進程使用,因此該進程無法訪問此文件”報錯信息。
記錄log信息:
Tid: 9 2021年5月21日 下午 02:18:04.919 Tid: 9 2021年5月21日 下午 02:18:04.944 Tid: 9 2021年5月21日 下午 02:18:05.80 Tid: 11 2021年5月21日 下午 02:18:05.81 Tid: 9 2021年5月21日 下午 02:18:05.82 Tid: 12 2021年5月21日 下午 02:18:05.83 Tid: 11 2021年5月21日 下午 02:18:05.84 Tid: 12 2021年5月21日 下午 02:18:05.84 Tid: 16 2021年5月21日 下午 02:18:05.85 Tid: 12 2021年5月21日 下午 02:18:05.111 Tid: 16 2021年5月21日 下午 02:18:05.117 Tid: 16 2021年5月21日 下午 02:18:05.128 Tid: 11 2021年5月21日 下午 02:18:05.128 Tid: 16 2021年5月21日 下午 02:18:05.133 Tid: 12 2021年5月21日 下午 02:18:05.138 Tid: 16 2021年5月21日 下午 02:18:05.140 Tid: 12 2021年5月21日 下午 02:18:05.140 Tid: 16 2021年5月21日 下午 02:18:05.142 Tid: 16 2021年5月21日 下午 02:18:05.144 Tid: 16 2021年5月21日 下午 02:18:05.151 Tid: 16 2021年5月21日 下午 02:18:05.158 Tid: 9 2021年5月21日 下午 02:18:05.159 Tid: 10 2021年5月21日 下午 02:18:05.159 Tid: 9 2021年5月21日 下午 02:18:05.164 Tid: 16 2021年5月21日 下午 02:18:05.164 Tid: 9 2021年5月21日 下午 02:18:05.172 Tid: 15 2021年5月21日 下午 02:18:05.172 Tid: 16 2021年5月21日 下午 02:18:05.181 Tid: 16 2021年5月21日 下午 02:18:05.187 Tid: 15 2021年5月21日 下午 02:18:05.188 Tid: 16 2021年5月21日 下午 02:18:05.195 Tid: 16 2021年5月21日 下午 02:18:05.196 Tid: 15 2021年5月21日 下午 02:18:05.195 Tid: 16 2021年5月21日 下午 02:18:05.202 Tid: 16 2021年5月21日 下午 02:18:05.203 Tid: 15 2021年5月21日 下午 02:18:05.202 Tid: 15 2021年5月21日 下午 02:18:05.207 Tid: 15 2021年5月21日 下午 02:18:05.209 Tid: 16 2021年5月21日 下午 02:18:05.207 Tid: 15 2021年5月21日 下午 02:18:05.210 Tid: 15 2021年5月21日 下午 02:18:05.222 Tid: 15 2021年5月21日 下午 02:18:05.231 Tid: 18 2021年5月21日 下午 02:18:05.238 Tid: 15 2021年5月21日 下午 02:18:05.238 Tid: 18 2021年5月21日 下午 02:18:05.244 Tid: 15 2021年5月21日 下午 02:18:05.251 Tid: 15 2021年5月21日 下午 02:18:05.256 Tid: 15 2021年5月21日 下午 02:18:05.262 Tid: 15 2021年5月21日 下午 02:18:05.304 Tid: 15 2021年5月21日 下午 02:18:05.312 Tid: 13 2021年5月21日 下午 02:18:05.312 Tid: 9 2021年5月21日 下午 02:18:05.313 Tid: 13 2021年5月21日 下午 02:18:05.320 Tid: 19 2021年5月21日 下午 02:18:05.320 Tid: 16 2021年5月21日 下午 02:18:05.325 Tid: 19 2021年5月21日 下午 02:18:05.333 Tid: 16 2021年5月21日 下午 02:18:05.342 Tid: 16 2021年5月21日 下午 02:18:05.349 Tid: 16 2021年5月21日 下午 02:18:05.361 Tid: 16 2021年5月21日 下午 02:18:05.366 Tid: 16 2021年5月21日 下午 02:18:05.367 Tid: 16 2021年5月21日 下午 02:18:05.368 Tid: 16 2021年5月21日 下午 02:18:05.376 Tid: 16 2021年5月21日 下午 02:18:05.386 Tid: 16 2021年5月21日 下午 02:18:05.392 Tid: 16 2021年5月21日 下午 02:18:05.401 Tid: 9 2021年5月21日 下午 02:18:05.463 Tid: 13 2021年5月21日 下午 02:18:05.464 Tid: 15 2021年5月21日 下午 02:18:05.464 Tid: 13 2021年5月21日 下午 02:18:05.465 Tid: 13 2021年5月21日 下午 02:18:05.470 Tid: 11 2021年5月21日 下午 02:18:05.479
三、使用讀寫鎖寫入文件:
代碼:
class Program { static int LogCount = 100; static int WritedCount = 0; static int FailedCount = 0; static void Main(string[] args) { //迭代運行寫入日志記錄,由于多個線程同時寫入同一個文件將會導致錯誤 Parallel.For(0, LogCount, e => { WriteLog2(); }); Console.WriteLine(string.Format("\r\nLog Count:{0}.\t\tWrited Count:{1}.\tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString())); Console.Read(); } #region 加入讀寫鎖 //讀寫鎖,當資源處于寫入模式時,其他線程寫入需要等待本次寫入結束之后才能繼續(xù)寫入 static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim(); static void WriteLog2() { try { //設置讀寫鎖為寫入模式獨占資源,其他寫入請求需要等待本次寫入結束之后才能繼續(xù)寫入 //注意:長時間持有讀線程鎖或寫線程鎖會使其他線程發(fā)生饑餓 (starve)。 為了得到最好的性能,需要考慮重新構造應用程序以將寫訪問的持續(xù)時間減少到最小。 //從性能方面考慮,請求進入寫入模式應該緊跟文件操作之前,在此處進入寫入模式僅是為了降低代碼復雜度 //因進入與退出寫入模式應在同一個try finally語句塊內,所以在請求進入寫入模式之前不能觸發(fā)異常,否則釋放次數(shù)大于請求次數(shù)將會觸發(fā)異常 LogWriteLock.EnterWriteLock(); var logFilePath = "log.txt"; var now = DateTime.Now; var logContent = string.Format("Tid: {0}{1} {2}.{3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString()); File.AppendAllText(logFilePath, logContent); WritedCount++; } catch (Exception) { FailedCount++; } finally { //退出寫入模式,釋放資源占用 //注意:一次請求對應一次釋放 //若釋放次數(shù)大于請求次數(shù)將會觸發(fā)異常[寫入鎖定未經(jīng)保持即被釋放] //若請求處理完成后未釋放將會觸發(fā)異常[此模式不下允許以遞歸方式獲取寫入鎖定] LogWriteLock.ExitWriteLock(); } } #endregion }
運行結果:
所有的log都完全正確寫入到log.txt。
記錄log信息:
Tid: 8 2021年5月21日 下午 02:26:36.573 Tid: 8 2021年5月21日 下午 02:26:36.597 Tid: 8 2021年5月21日 下午 02:26:36.599 Tid: 8 2021年5月21日 下午 02:26:36.600 Tid: 8 2021年5月21日 下午 02:26:36.601 Tid: 8 2021年5月21日 下午 02:26:36.602 Tid: 8 2021年5月21日 下午 02:26:36.608 Tid: 8 2021年5月21日 下午 02:26:36.609 Tid: 8 2021年5月21日 下午 02:26:36.614 Tid: 8 2021年5月21日 下午 02:26:36.616 Tid: 8 2021年5月21日 下午 02:26:36.617 Tid: 8 2021年5月21日 下午 02:26:36.620 Tid: 8 2021年5月21日 下午 02:26:36.620 Tid: 8 2021年5月21日 下午 02:26:36.621 Tid: 8 2021年5月21日 下午 02:26:36.622 Tid: 8 2021年5月21日 下午 02:26:36.623 Tid: 8 2021年5月21日 下午 02:26:36.624 Tid: 8 2021年5月21日 下午 02:26:36.624 Tid: 8 2021年5月21日 下午 02:26:36.625 Tid: 8 2021年5月21日 下午 02:26:36.626 Tid: 8 2021年5月21日 下午 02:26:36.626 Tid: 8 2021年5月21日 下午 02:26:36.627 Tid: 8 2021年5月21日 下午 02:26:36.628 Tid: 8 2021年5月21日 下午 02:26:36.628 Tid: 8 2021年5月21日 下午 02:26:36.629 Tid: 8 2021年5月21日 下午 02:26:36.630 Tid: 8 2021年5月21日 下午 02:26:36.630 Tid: 8 2021年5月21日 下午 02:26:36.631 Tid: 8 2021年5月21日 下午 02:26:36.632 Tid: 8 2021年5月21日 下午 02:26:36.632 Tid: 8 2021年5月21日 下午 02:26:36.633 Tid: 8 2021年5月21日 下午 02:26:36.634 Tid: 8 2021年5月21日 下午 02:26:36.634 Tid: 8 2021年5月21日 下午 02:26:36.635 Tid: 8 2021年5月21日 下午 02:26:36.636 Tid: 8 2021年5月21日 下午 02:26:36.636 Tid: 8 2021年5月21日 下午 02:26:36.637 Tid: 8 2021年5月21日 下午 02:26:36.638 Tid: 8 2021年5月21日 下午 02:26:36.638 Tid: 8 2021年5月21日 下午 02:26:36.639 Tid: 8 2021年5月21日 下午 02:26:36.641 Tid: 8 2021年5月21日 下午 02:26:36.641 Tid: 8 2021年5月21日 下午 02:26:36.642 Tid: 8 2021年5月21日 下午 02:26:36.643 Tid: 8 2021年5月21日 下午 02:26:36.644 Tid: 8 2021年5月21日 下午 02:26:36.644 Tid: 8 2021年5月21日 下午 02:26:36.645 Tid: 8 2021年5月21日 下午 02:26:36.646 Tid: 8 2021年5月21日 下午 02:26:36.647 Tid: 8 2021年5月21日 下午 02:26:36.647 Tid: 8 2021年5月21日 下午 02:26:36.648 Tid: 8 2021年5月21日 下午 02:26:36.649 Tid: 8 2021年5月21日 下午 02:26:36.650 Tid: 8 2021年5月21日 下午 02:26:36.650 Tid: 8 2021年5月21日 下午 02:26:36.651 Tid: 8 2021年5月21日 下午 02:26:36.652 Tid: 8 2021年5月21日 下午 02:26:36.652 Tid: 8 2021年5月21日 下午 02:26:36.652 Tid: 8 2021年5月21日 下午 02:26:36.653 Tid: 8 2021年5月21日 下午 02:26:36.654 Tid: 8 2021年5月21日 下午 02:26:36.655 Tid: 8 2021年5月21日 下午 02:26:36.656 Tid: 8 2021年5月21日 下午 02:26:36.658 Tid: 8 2021年5月21日 下午 02:26:36.658 Tid: 8 2021年5月21日 下午 02:26:36.659 Tid: 8 2021年5月21日 下午 02:26:36.660 Tid: 8 2021年5月21日 下午 02:26:36.660 Tid: 8 2021年5月21日 下午 02:26:36.661 Tid: 8 2021年5月21日 下午 02:26:36.662 Tid: 8 2021年5月21日 下午 02:26:36.662 Tid: 8 2021年5月21日 下午 02:26:36.663 Tid: 8 2021年5月21日 下午 02:26:36.664 Tid: 8 2021年5月21日 下午 02:26:36.664 Tid: 8 2021年5月21日 下午 02:26:36.665 Tid: 8 2021年5月21日 下午 02:26:36.666 Tid: 8 2021年5月21日 下午 02:26:36.666 Tid: 8 2021年5月21日 下午 02:26:36.667 Tid: 8 2021年5月21日 下午 02:26:36.668 Tid: 8 2021年5月21日 下午 02:26:36.669 Tid: 8 2021年5月21日 下午 02:26:36.669 Tid: 8 2021年5月21日 下午 02:26:36.670 Tid: 8 2021年5月21日 下午 02:26:36.671 Tid: 8 2021年5月21日 下午 02:26:36.672 Tid: 8 2021年5月21日 下午 02:26:36.673 Tid: 8 2021年5月21日 下午 02:26:36.675 Tid: 8 2021年5月21日 下午 02:26:36.675 Tid: 8 2021年5月21日 下午 02:26:36.676 Tid: 8 2021年5月21日 下午 02:26:36.677 Tid: 14 2021年5月21日 下午 02:26:36.678 Tid: 15 2021年5月21日 下午 02:26:36.679 Tid: 16 2021年5月21日 下午 02:26:36.680 Tid: 17 2021年5月21日 下午 02:26:36.681 Tid: 18 2021年5月21日 下午 02:26:36.681 Tid: 20 2021年5月21日 下午 02:26:36.683 Tid: 9 2021年5月21日 下午 02:26:36.683 Tid: 19 2021年5月21日 下午 02:26:36.684 Tid: 10 2021年5月21日 下午 02:26:36.685 Tid: 11 2021年5月21日 下午 02:26:36.685 Tid: 12 2021年5月21日 下午 02:26:36.687 Tid: 13 2021年5月21日 下午 02:26:36.688
到此這篇關于C#使用讀寫鎖解決多線程并發(fā)問題的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
c#實現(xiàn)用SQL池,多線程定時批量執(zhí)行SQL語句的方法
構建SQL池,分離業(yè)務邏輯層和數(shù)據(jù)訪問層,讓業(yè)務邏輯層從低效的數(shù)據(jù)庫操作解脫,以提高系統(tǒng)整體性能2013-10-10