您好,登錄后才能下訂單哦!
這篇文章主要講解了C#如何實現線程同步,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
一、進程內部的線程同步
1、使用lock,用法如下:
private static readonly object SeqLock = new object(); private void Print() { lock (SeqLock) { Console.WriteLine("test"); } }
特性:只能傳遞對象,無法設置等待超時
2、使用:InterLocked(原子操作)
其在System.Threading命名空間下,Interlocked實際是類控制計數器,從而實現進程的同步,其很容易實現生產者消費者模型
//緩沖區(qū),只能容納一個字符 private static char buffer; //標識量(緩沖區(qū)中已使用的空間,初始值為0) private static long numberOfUsedSpace = 0; static void Main(string[] args) { //線程:寫入者 Thread Writer = new Thread(delegate () { string str = "這里面的字會一個一個讀取出來,一個都不會少,,,"; for (int i = 0; i < 24; i++) { //寫入數據前檢查緩沖區(qū)是否已滿 //如果已滿,就進行等待,直到緩沖區(qū)中的數據被進程Reader讀取為止 while (Interlocked.Read(ref numberOfUsedSpace) == 1) { Thread.Sleep(50); } buffer = str[i]; //向緩沖區(qū)寫入數據 //寫入數據后把緩沖區(qū)標記為滿(由0變?yōu)?) Interlocked.Increment(ref numberOfUsedSpace); } }); //線程:讀出者 Thread Reader = new Thread(delegate () { for (int i = 0; i < 24; i++) { //讀取數據前檢查緩沖區(qū)是否為空 //如果為空,就進行等待,直到進程Writer向緩沖區(qū)中寫入數據為止 while (Interlocked.Read(ref numberOfUsedSpace) == 0) { Thread.Sleep(50); } char ch = buffer; //從緩沖區(qū)讀取數據 Console.Write(ch); Interlocked.Decrement(ref numberOfUsedSpace); } }); //啟動線程 Writer.Start(); Reader.Start(); Console.ReadKey();
3、使用Monitor
其中Monitor.Enter()和lock相同
Monitor.Enter(obj){ //Synchronized part }finally{ Monitor.Exit(obj); }
TryEnter則可設置等待時間等
bool lockTaken=false; Monitor.TryEnter(obj, 500, ref lockTaken); if(lockTaken){ try { //Synchronized part } finally { Monitor.Exit(obj); } }else{ //don't aquire the lock, excute other parts }
二、進程間的同步
1. WaitHandle:
封裝等待對共享資源進行獨占訪問的操作系統(tǒng)特定的對象。 WaitHandle:是一個抽象類,我們一般不直接用,而是用它的派生類:
AutoResetEvent、EventWaitHandle、ManualResetEvent、Mutex、Semaphore
這個抽象類的方法如下:
WaitOne(): 等待一個信號的出現,可設置超時;
WaitAll(): 等待多個信號的出現,可設置超時;
WaitAny(): 等待任意一個信號的出現,可設置超時;
2、Mutex: 與Monitor 類似,只有一個線程能夠獲取鎖定。利用WaitOne() 獲取鎖定,利用ReleaseMutex() 解除鎖定。構造函數使用如下:
bool isNew = false; mutex = new Mutex(false, "Mutex1", out isNew);
參數1:鎖創(chuàng)建后是否由主調線程擁有。 如果設為true,相當于調用了WaitOne(),需要釋放,否則其他線程無法獲取鎖;
參數2:鎖名稱,可通過OpenExist()或TryOpenExist() 打開已有鎖,因為操作系統(tǒng)識別有名稱的互鎖,所以可由不同的進程共享。若鎖名稱為空,就是未命名的互鎖,不能在多個進程之間共享;
參數3: 是否為新創(chuàng)建的互鎖;
下面的例子演示Mutex 在進程之間的使用: class Program
private static Mutex mutex = null; static void Main(string[] args) { bool isNew = false; mutex = new Mutex(false, "Mutex1", out isNew); Console.WriteLine("Main Start...."); mutex.WaitOne(); Console.WriteLine("Aquire Lock and Running...."); Thread.Sleep(10000); mutex.ReleaseMutex(); Console.WriteLine("Release Lock...."); Console.WriteLine("Main end...."); Console.ReadLine(); } }
連續(xù)2次運行這個控制臺程序的exe,結果如下,首先運行的獲取 Mutex1 互鎖, 后面運行的會等待直到前面運行的釋放 Mutex1 互鎖。
3.Semaphore: 信號量的作用于互斥鎖類似,但它可以定義一定數量的線程同時使用。下面是構造函數:
bool isNew = false; semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
參數1:創(chuàng)建后,最初釋放的鎖的數量,如參數1設為2,參數2設為3,則創(chuàng)建后只有2個鎖可用,另1個已經鎖定;
參數2:定義可用鎖的數量;
參數3: 信號量的名稱,與Mutex類似;
參數4:是否為新創(chuàng)建的互鎖;
以下例子創(chuàng)建了信號量“semaphore1”,利用Parallel.For() 同步運行Func1() ,在Func1() 中,當線程獲取信號量鎖,釋放鎖或等待超時,都會在控制臺里輸出,
class Program { private static Semaphore semaphore = null; static void Main(string[] args) { Console.WriteLine("Main Start...."); bool isNew = false; semaphore = new Semaphore(3, 3, "semaphore1", out isNew); Parallel.For(0, 6, Func1); Console.WriteLine("Main end...."); Console.ReadLine(); } static void Func1(int index) { Console.WriteLine("Task {0} Start....",Task.CurrentId); bool isComplete = false; while (!isComplete) { if (semaphore.WaitOne(1000)) { try { Console.WriteLine("Task {0} aquire lock....", Task.CurrentId); Thread.Sleep(5000); } finally { semaphore.Release(); Console.WriteLine("Task {0} release lock....", Task.CurrentId); isComplete = true; } } else { Console.WriteLine("Task {0} timeout....", Task.CurrentId); } } }
運行結果如下,線程1,2,3首先獲取信號量鎖,線程4,5,6在等待,直到1,2,3釋放,
4. AutoResetEvent 類:
可以使用事件通知其他任務,構造函數為 public AutoResetEvent(bool initialState)。
當initialState=true,處于signaled 模式(終止狀態(tài)),調用waitone() 也不會阻塞任務,等待信號,調用Reset()方法,可以設置為non-signaled 模式;
當initialState=fasle,處于non-signaled 模式(非終止狀態(tài)),調用waitone() 會等待信號阻塞當前線程(可以在多個線程中調用,同時阻塞多個線程),直到調用set()發(fā)送信號釋放線程(調用一次,只能釋放一個線程),一般使用這種方式;
以下例子創(chuàng)建5個任務,分別調用waitone()阻塞線程,接著每隔2s 調用set(),
private static AutoResetEvent autoReset = new AutoResetEvent(false); static void Main(string[] args) { Console.WriteLine("Main Start...."); for (int i = 0; i < 5; i++) { Task.Factory.StartNew(() => { Console.WriteLine("{0} Start....", Task.CurrentId); autoReset.WaitOne(); Console.WriteLine("{0} Continue....", Task.CurrentId); }); } for (int i = 0; i < 5;i++ ) { Thread.Sleep(2000); autoReset.Set(); } Console.WriteLine("Main end...."); Console.ReadLine(); }
運行結果每次順序略有不同,釋放是隨機的:
5. ManualResetEvent 類:功能基本上和AutoSetEvent類似,但又一個不同點:
使用AutoSetEvent,每次調用set(),切換到終止模式,只能釋放一個waitone(),便會自動切換到非終止模式;但ManualResetEvent,調用set(),切換到終止模式,可以釋放當前所有的waitone(),需要手動調用reset()才能切換到非終止模式。
以下例子說明了這個不同的:
private static ManualResetEvent manualReset = new ManualResetEvent(false); static void Main(string[] args) { Console.WriteLine("Main Start...."); for (int i = 0; i < 5; i++) { Task.Factory.StartNew(() => { Console.WriteLine("{0} Start....", Task.CurrentId); manualReset.WaitOne(); Console.WriteLine("{0} Continue....", Task.CurrentId); }); } Thread.Sleep(2000); manualReset.Set(); manualReset.WaitOne(); Console.WriteLine("it doesn't work now, Main continue...."); manualReset.Reset(); manualReset.WaitOne(); Console.WriteLine("Main end...."); Console.ReadLine(); }
看完上述內容,是不是對C#如何實現線程同步有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。