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的使用。