多線程學習筆記之線程同步

1、爲多個線程共享的變量提供原子操作。Interlocked

public static class Interlocked

2、互斥鎖Mutex 限制只能有一個訪問

		Mutex m = new Mutex();			
		m.WaitOne();
        Console.WriteLine("1");
        m.ReleaseMutex();

3、多個訪問線程數量鎖SemaphoreSlim

		static SemaphoreSlim _semaphore = new SemaphoreSlim(4);//限制4個線程最多同時訪問
		//下面方法佔用一個
		_semaphore.Wait();

        _semaphore.Release();

4、線程間通知AutoResetEvent

AutoResetEvent myResetEvent = new AutoResetEvent(false);

    如過設置true,那麼myResetEvent.WaitOne()就能夠在程序一啓動就獲得運行權;就相當於一啓動程序就自動運行了一次myResetEven.Set();
    否則myResetEven.Set()成功運行後,myResetEven.WaitOne()才能夠獲得運行機會;
false:無信號,子線程的WaitOne方法不會被自動調用
true:有信號,子線程的WaitOne方法會被自動調用myResetEvent.WaitOne();
    當調用myResetEvent.Set();後,waitone後的代碼才執行。WaitOne 或是WaitAll 最好都加上超時時間。
    AutoResetEvent在.Net多線程編程中,經常用到。當某個線程調用WaitOne方法後,信號處於發送狀態,該線程會得到信號, 程序就會繼續向下執行,否則就等待。而且 AutoResetEvent.WaitOne()每次只允許一個線程進入,當某個線程得到信號後,AutoResetEvent會自動又將信號置爲不發送狀態,其他調用WaitOne的線程只有繼續等待.也就是說,AutoResetEvent一次只喚醒一個線程,其他線程還是堵塞。(!!!只能通知到單個線程)
    Reset ():將事件狀態設置爲非終止狀態,導致線程阻止;如果該操作成功,則返回true;否則,返回false。
    Set ():將事件狀態設置爲終止狀態,允許一個或多個等待線程繼續;如果該操作成功,則返回true;否則,返回false。
    WaitOne(): 阻止當前線程,直到收到信號。
    WaitOne(TimeSpan, Boolean) :阻止當前線程,直到當前實例收到信號,使用 TimeSpan 度量時間間隔並指定是否在等待之前退出同步域。
    WaitAll():等待全部信號。
注意:AutoResetEvent採用的是內核時間,所以等待時間不能太長。使用後面的ManualResetEventSlim類更好

5、多線程間通知ManualResetEvent

    與AutoResetEvent類似,但是ManualResetEvent是通知所有準備好的線程。ManualResetEvnetSlim的整個工作方式有點像人羣通過大門。
    AutoResetEvent事件像一個旋轉門,一次只允許一人通過。ManualResetEventSlim是Manual-ResetEvent的混合版本,一直保持大門敞開直到手動調用Reset方法。當調用_mainEvent.Set時,相當於打開了大門從而允許準備好的線程接收信號並繼續工作。然而如果線程還處於睡眠狀態,沒有趕上時間。當調用_mainEvent.Reset相當於關閉了大門。最後一個線程已經準備好執行,但是不得不等待下一個信號,即要等待好幾秒鐘。

6、CountDownEvent等待一定數量操作的類

		// <summary>
        /// 創建 CountdownEvent 實例,指定達到次數爲 2 次
        /// </summary>
        public static CountdownEvent _countdown = new CountdownEvent(2);

        /// <summary>
        ///
        /// </summary>
        /// <param name="message">消息提示</param>
        /// <param name="seconds">時間(秒)</param>
        public static void PerformOperation(string message, int seconds)
        {
            System.Threading.Thread.Sleep(TimeSpan.FromSeconds(seconds));

            Console.WriteLine(message);
			//添加一次執行數量
            _countdown.Signal();
        }
		
		/// <summary>
        /// 輸出
        /// </summary>
        public static void Print()
        {
            // Starting two operations

            var t1 = new System.Threading.Thread(() => PerformOperation("Operation 1 is completed", 4));

            var t2 = new System.Threading.Thread(() => PerformOperation("Operation 2 is completed", 8));

            t1.Start();
            t2.Start();

            // 如果 調用 _countdown.Signal() 沒達到指定的次數
            // 那麼 _countdown.Signal() 將一直阻止,繼續等待
            _countdown.Wait();  // 阻止當前線程

            // Both operation have been completed
            _countdown.Dispose();   // 釋放資源
        }

7、Barrier 線程指定次數調用SignalAndWait後,自動調用回調函數Barrier

		static void Main(string[] args)
        {
            var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5));
            var t2 = new Thread(() => PlayMusic("the singer", "sing the song", 2));
 
            t1.Start();
            t2.Start();
 
            Console.ReadKey();
        }
		//指定Barrier在線程調用兩次SignalAndWait後執行回調函數。
        static Barrier _barrier = new Barrier(2, b => Console.WriteLine("End of phase {0}", b.CurrentPhaseNumber + 1));
 
        static void PlayMusic(string name,string message,int seconds)
        {
            for (int i = 1; i < 3; i++)
            {
                Console.WriteLine("---------------------------------------------");
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("{0} starts to {1}", name, message);
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine("{0} finishes to {1}", name, message);
                _barrier.SignalAndWait();
            }
        }

8、ReaderWriterLockSlim 線程對集合讀寫的安全機制,允許多線程同時讀取,以及獨佔用

    ReaderWriterLockSlim 類支持三種鎖定模式:Read,Write,UpgradeableRead。這三種模式對應的方法分別是 EnterReadLock,EnterWriteLock,EnterUpgradeableReadLock 。再就是與此對應的 TryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。
    Read 和 Writer 鎖定模式比較簡單易懂:Read 模式是典型的共享鎖定模式,任意數量的線程都可以在該模式下同時獲得鎖;Writer 模式則是互斥模式,在該模式下只允許一個線程進入該鎖。UpgradeableRead 鎖定模式可能對於大多數人來說比較新鮮,但是在數據庫領域卻衆所周知。
備註及注意事項
1、對於同一把鎖、多個線程可同時進入讀模式。
2、對於同一把鎖、同時只允許一個線程進入寫模式。
3、對於同一把鎖、同時只允許一個線程進入可升級的讀模式。
4、通過默認構造函數創建的讀寫鎖是不支持遞歸的,若想支持遞歸 可通過構造 ReaderWriterLockSlim(LockRecursionPolicy) 創建實例。
5、對於同一把鎖、同一線程不可兩次進入同一鎖狀態(開啓遞歸後可以)
6、對於同一把鎖、即便開啓了遞歸、也不可以在進入讀模式後再次進入寫模式或者可升級的讀模式(在這之前必須退出讀模式)。
7、再次強調、不建議啓用遞歸。
8、讀寫鎖具有線程關聯性,即兩個線程間擁有的鎖的狀態相互獨立不受影響、並且不能相互修改其鎖的狀態。
9、升級狀態:在進入可升級的讀模式 EnterUpgradeableReadLock後,可在恰當時間點通過EnterWriteLock進入寫模式。
10、降級狀態:可升級的讀模式可以降級爲讀模式:即在進入可升級的讀模式EnterUpgradeableReadLock後, 通過首先調用讀取模式EnterReadLock方法,然後再調用 ExitUpgradeableReadLock 方法。
    簡單的說,當某個線程進入讀取模式時,此時其他線程依然能進入讀取模式,假設此時一個線程要進入寫入模式,那麼他不得不被阻塞。直到讀取模式退出爲止。
    同樣的,如果某個線程進入了寫入模式,那麼其他線程無論是要寫入還是讀取,都是會被阻塞的。
進入寫入/讀取模式有2種方法:
    EnterReadLock嘗試進入寫入模式鎖定狀態。
    TryEnterReadLock(Int32) 嘗試進入讀取模式鎖定狀態,可以選擇整數超時時間。
    EnterWriteLock 嘗試進入寫入模式鎖定狀態。
    TryEnterWriteLock(Int32) 嘗試進入寫入模式鎖定狀態,可以選擇超時時間。
退出寫入/讀取模式有2種方法:
    ExitReadLock 減少讀取模式的遞歸計數,並在生成的計數爲 0(零)時退出讀取模式。
    ExitWriteLock 減少寫入模式的遞歸計數,並在生成的計數爲 0(零)時退出寫入模式。
    爲了降低阻塞,可以使用EnterUpgradeableReadLock和ExitUpgradeableReadLock方法,先獲取讀鎖後讀取數據,如發現必須修改,只需要使用EnterWriteLock升級鎖,然後快速的進行一次寫操作,最後使用ExitWriteLock釋放寫鎖。

    ReaderWriterLockSlim類提供了可升級讀模式,這種方式和讀模式的區別在於它還有通過調用 EnterWriteLock 或 TryEnterWriteLock 方法升級爲寫入模式。 因爲每次只能有一個線程處於可升級模式。進入可升級模式的線程,不會影響讀取模式的線程,即當一個線程進入可升級模式,任意數量線程可以同時進入讀取模式,不會阻塞。如果有多個線程已經在等待獲取寫入鎖,那麼運行EnterUpgradeableReadLock將會阻塞,直到那些線程超時或者退出寫入鎖。

9、SpinWait 混合同步線程等待

它是一個混合同步構造,被設計爲使用用戶模式等待一段時間,然後切換到內核模式以節省CPU時間。

		//初始化
		var w = new SpinWait();	
		//添加一次迭代
		w.SpinOnce();

    我們使用了SpinWait版本,剛開始,SpinWait嘗試使用用戶模式,在9個迭//代後,開始切換線程爲阻塞狀態。如果嘗試測量該版本的CPU負載,在Windows任務管理器將不會看到任何CPU的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章