<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
程式碼一:
class Program { static int LogCount = 1000; static int SumLogCount = 0; static int WritedCount = 0; static int FailedCount = 0; static void Main(string[] args) { //往執行緒池裡新增一個任務,迭代寫入N個紀錄檔 SumLogCount += LogCount; ThreadPool.QueueUserWorkItem((obj) => { Parallel.For(0, LogCount, e => { WriteLog(); }); }); //在新的執行緒裡,新增N個寫入紀錄檔的任務到執行緒池 SumLogCount += LogCount; var thread1 = new Thread(() => { Parallel.For(0, LogCount, e => { ThreadPool.QueueUserWorkItem((subObj) => { WriteLog(); }); }); }); thread1.IsBackground = false; thread1.Start(); //新增N個寫入紀錄檔的任務到執行緒池 SumLogCount += LogCount; Parallel.For(0, LogCount, e => { ThreadPool.QueueUserWorkItem((obj) => { WriteLog(); }); }); //在新的執行緒裡,迭代寫入N個紀錄檔 SumLogCount += LogCount; var thread2 = new Thread(() => { Parallel.For(0, LogCount, e => { WriteLog(); }); }); thread2.IsBackground = false; thread2.Start(); //在當前執行緒裡,迭代寫入N個紀錄檔 SumLogCount += LogCount; Parallel.For(0, LogCount, e => { WriteLog(); }); Console.WriteLine("Main Thread Processed.rn"); while (true) { Console.WriteLine(string.Format("Sum Log Count:{0}.ttWrited Count:{1}.tFailed Count:{2}.", SumLogCount.ToString(), WritedCount.ToString(), FailedCount.ToString())); Console.ReadLine(); } } //讀寫鎖,當資源處於寫入模式時,其他執行緒寫入需要等待本次寫入結束之後才能繼續寫入 static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim(); static void WriteLog() { try { //設定讀寫鎖為寫入模式獨佔資源,其他寫入請求需要等待本次寫入結束之後才能繼續寫入 //注意:長時間持有讀執行緒鎖或寫執行緒鎖會使其他執行緒發生飢餓 (starve)。 為了得到最好的效能,需要考慮重新構造應用程式以將寫存取的持續時間減少到最小。 //從效能方面考慮,請求進入寫入模式應該緊跟檔案操作之前,在此處進入寫入模式僅是為了降低程式碼複雜度 //因進入與退出寫入模式應在同一個try finally語句塊內,所以在請求進入寫入模式之前不能觸發異常,否則釋放次數大於請求次數將會觸發異常 LogWriteLock.EnterWriteLock(); var logFilePath = "log.txt"; var now = DateTime.Now; var logContent = string.Format("Tid: {0}{1} {2}.{3}rn", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString()); File.AppendAllText(logFilePath, logContent); WritedCount++; } catch (Exception) { FailedCount++; } finally { //退出寫入模式,釋放資源佔用 //注意:一次請求對應一次釋放 // 若釋放次數大於請求次數將會觸發異常[寫入鎖定未經保持即被釋放] // 若請求處理完成後未釋放將會觸發異常[此模式不下允許以遞迴方式獲取寫入鎖定] LogWriteLock.ExitWriteLock(); } } }
執行結果:
複雜多執行緒環境下使用讀寫鎖,全部紀錄檔成功寫入了紀錄檔檔案,由ThreadId和DateTime可以看出是由不同的執行緒同步寫入。
程式碼二:
class Program { static void Main(string[] args) { #region 簡單使用 //var mutexKey = MutexExample.GetFilePathMutexKey("檔案路徑"); //MutexExample.MutexExec(mutexKey, () => //{ // Console.WriteLine("需要程序同步執行的程式碼"); //}); #endregion #region 測試程式碼 //D:Winform多執行緒多執行緒_互斥訊號量binDebugTEST.LOG var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.log").ToUpper(); var mutexKey = MutexExample.GetFilePathMutexKey(filePath); //同時開啟N個寫入執行緒 Parallel.For(0, LogCount, e => { //沒使用互斥鎖操作寫入,大量寫入錯誤;FileStream包含FileShare的建構函式也僅實現了程序內的執行緒同步,多程序同時寫入時也會出錯 //WriteLog(filePath); //使用互斥鎖操作寫入,由於同一時間僅有一個執行緒操作,所以不會出錯 MutexExample.MutexExec(mutexKey, () => { WriteLog(filePath); }); }); Console.WriteLine(string.Format("Log Count:{0}.ttWrited Count:{1}.tFailed Count:{2}.", LogCount.ToString(), WritedCount.ToString(), FailedCount.ToString())); Console.Read(); #endregion } /// <summary> /// C#互斥量使用範例程式碼 /// </summary> /// <remarks>已在經過測試並上線執行,可直接使用</remarks> public static class MutexExample { /// <summary> /// 程序間同步執行的簡單例子 /// </summary> /// <param name="action">同步處理程式碼</param> /// <param name="mutexKey">作業系統級的同步鍵 /// (如果將 name 指定為 null 或空字串,則建立一個區域性互斥體。 /// 如果名稱以字首「Global」開頭,則 mutex 在所有終端伺服器對談中均為可見。 /// 如果名稱以字首「Local」開頭,則 mutex 僅在建立它的終端伺服器對談中可見。 /// 如果建立已命名 mutex 時不指定字首,則它將採用字首「Local」。)</param> /// <remarks>不重試且不考慮異常情況處理的簡單例子</remarks> [Obsolete(error: false, message: "請使用MutexExec")] public static void MutexExecEasy(string mutexKey, Action action) { //宣告一個已命名的互斥體,實現程序間同步;該命名互斥體不存在則自動建立,已存在則直接獲取 using (Mutex mut = new Mutex(false, mutexKey)) { try { //上鎖,其他執行緒需等待釋放鎖之後才能執行處理;若其他執行緒已經上鎖或優先上鎖,則先等待其他執行緒執行完畢 mut.WaitOne(); //執行處理程式碼(在呼叫WaitHandle.WaitOne至WaitHandle.ReleaseMutex的時間段裡,只有一個執行緒處理,其他執行緒都得等待釋放鎖後才能執行該程式碼段) action(); } finally { //釋放鎖,讓其他程序(或執行緒)得以繼續執行 mut.ReleaseMutex(); } } } /// <summary> /// 獲取檔名對應的程序同步鍵 /// </summary> /// <param name="filePath">檔案路徑(請注意大小寫及空格)</param> /// <returns>程序同步鍵(互斥體名稱)</returns> public static string GetFilePathMutexKey(string filePath) { //生成檔案對應的同步鍵,可自定義格式(互斥體名稱對特殊字元支援不友好,遂轉換為BASE64格式字串) var fileKey = Convert.ToBase64String(Encoding.Default.GetBytes(string.Format(@"FILE{0}", filePath))); //轉換為作業系統級的同步鍵 var mutexKey = string.Format(@"Global{0}", fileKey); return mutexKey; } /// <summary> /// 程序間同步執行 /// </summary> /// <param name="mutexKey">作業系統級的同步鍵 /// (如果將 name 指定為 null 或空字串,則建立一個區域性互斥體。 /// 如果名稱以字首「Global」開頭,則 mutex 在所有終端伺服器對談中均為可見。 /// 如果名稱以字首「Local」開頭,則 mutex 僅在建立它的終端伺服器對談中可見。 /// 如果建立已命名 mutex 時不指定字首,則它將採用字首「Local」。)</param> /// <param name="action">同步處理操作</param> public static void MutexExec(string mutexKey, Action action) { MutexExec(mutexKey: mutexKey, action: action, recursive: false); } /// <summary> /// 程序間同步執行 /// </summary> /// <param name="mutexKey">作業系統級的同步鍵 /// (如果將 name 指定為 null 或空字串,則建立一個區域性互斥體。 /// 如果名稱以字首「Global」開頭,則 mutex 在所有終端伺服器對談中均為可見。 /// 如果名稱以字首「Local」開頭,則 mutex 僅在建立它的終端伺服器對談中可見。 /// 如果建立已命名 mutex 時不指定字首,則它將採用字首「Local」。)</param> /// <param name="action">同步處理操作</param> /// <param name="recursive">指示當前呼叫是否為遞迴處理,遞迴處理時檢測到異常則丟擲異常,避免進入無限遞迴</param> private static void MutexExec(string mutexKey, Action action, bool recursive) { //宣告一個已命名的互斥體,實現程序間同步;該命名互斥體不存在則自動建立,已存在則直接獲取 //initiallyOwned: false:預設當前執行緒並不擁有已存在互斥體的所屬權,即預設本執行緒並非為首次建立該命名互斥體的執行緒 //注意:並行宣告同名的命名互斥體時,若間隔時間過短,則可能同時宣告了多個名稱相同的互斥體,並且同名的多個互斥體之間並不同步,高並行使用者請另行處理 using (Mutex mut = new Mutex(initiallyOwned: false, name: mutexKey)) { try { //上鎖,其他執行緒需等待釋放鎖之後才能執行處理;若其他執行緒已經上鎖或優先上鎖,則先等待其他執行緒執行完畢 mut.WaitOne(); //執行處理程式碼(在呼叫WaitHandle.WaitOne至WaitHandle.ReleaseMutex的時間段裡,只有一個執行緒處理,其他執行緒都得等待釋放鎖後才能執行該程式碼段) action(); } //當其他程序已上鎖且沒有正常釋放互斥鎖時(譬如程序忽然關閉或退出),則會丟擲AbandonedMutexException異常 catch (AbandonedMutexException ex) { //避免進入無限遞迴 if (recursive) throw ex; //非遞迴呼叫,由其他程序丟擲互斥鎖解鎖異常時,重試執行 MutexExec(mutexKey: mutexKey, action: action, recursive: true); } finally { //釋放鎖,讓其他程序(或執行緒)得以繼續執行 mut.ReleaseMutex(); } } } } #region 測試寫檔案的程式碼 static int LogCount = 500; static int WritedCount = 0; static int FailedCount = 0; static void WriteLog(string logFilePath) { try { var now = DateTime.Now; var logContent = string.Format("Tid: {0}{1} {2}.{3}rn", Thread.CurrentThread.ManagedThreadId.ToString().PadRight(4), now.ToLongDateString(), now.ToLongTimeString(), now.Millisecond.ToString()); File.AppendAllText(logFilePath, logContent); WritedCount++; } catch (Exception ex) { Console.WriteLine(ex.Message); FailedCount++; } } #endregion }
執行結果:
到此這篇關於C#在複雜多執行緒環境下使用讀寫鎖同步寫入檔案的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45