本文主要描述在C#中線程同步的方法。線程的基本概念網上資料也不少就再也不贅述了。直接接入主題,在多線程開發的應用中,線程同步是不可避免的。在.Net框架中,實現線程同步主要經過如下的幾種方式來實現,在MSDN的線程指南中已經講了幾種,本文結合做者實際中用到的方式一塊兒說明一下。 多線程
1. 維護自由鎖(InterLocked)實現同步
2. 監視器(Monitor)和互斥鎖(lock)
3. 讀寫鎖(ReadWriteLock)
4. 系統內核對象
1) 互斥(Mutex), 信號量(Semaphore), 事件(AutoResetEvent/ManualResetEvent)
2) 線程池
除了以上的這些對象以外實現線程同步的還可使用Thread.Join方法。這種方法比較簡單,當你在第一個線程運行時想等待第二個線程執行結果,那麼你可讓第二個線程Join進來就能夠了。
自由鎖(InterLocked)
對一個32位的整型數進行遞增和遞減操做來實現鎖,有人會問爲何不用++或--來操做。由於在多線程中對鎖進行操做必須是原子的,而++和--不具有這個能力。InterLocked類還提供了兩個另外的函數Exchange, CompareExchange用於實現交換和比較交換。Exchange操做會將新值設置到變量中並返回變量的原來值: int oVal = InterLocked.Exchange(ref val, 1)。
監視器(Monitor)
在MSDN中對Monitor的描述是: Monitor 類經過向單個線程授予對象鎖來控制對對象的訪問。
Monitor類是一個靜態類所以你不能經過實例化來獲得類的對象。Monitor的成員能夠查看MSDN,基本上Monitor的效果和lock是同樣的,經過加鎖操做Enter設置臨界區,完成操做後使用Exit操做來釋放對象鎖。不過相對來講Monitor的功能更強,Moniter能夠進行測試鎖的狀態,所以你能夠控制對臨界區的訪問選擇,等待or離開, 並且Monitor還能夠在釋放鎖以前通知指定的對象,更重要的是使用Monitor能夠跨越方法來操做。Monitor提供的方法不多就只有獲取鎖的方法Enter, TryEnter;釋放鎖的方法Wait, Exit;還有消息通知方法Pulse, PulseAll。經典的Monitor操做是這樣的:
框架
// 通監視器來建立臨界區
static public void DelUser(string name)
{
try
{
// 等待線程進入
Monitor.Enter(Names);
Names.Remove(name);
Console.WriteLine("Del: {0}", Names.Count);
Monitor.Pulse(Names);
}
finally
{
// 釋放對象鎖
Monitor.Exit(Names);
}
}
}ide
其中Names是一個List<string>, 這裏有一個小技巧,若是你想聲明整個方法爲線程同步可使用方法屬性:函數
// 經過屬性設置整個方法爲臨界區
[MethodImpl(MethodImplOptions.Synchronized)]
static public void AddUser(string name)
{
Names.Add(name);
Console.WriteLine("Add: {0}",Names.Count);
}測試
對於Monitor的使用有一個方法是比較詭異的,那就是Wait方法。在MSDN中對Wait的描述是: 釋放對象上的鎖以便容許其餘線程鎖定和訪問該對象。
這裏提到的是先釋放鎖,那麼顯然咱們須要先獲得鎖,不然調用Wait會出現異常,因此咱們必須在Wait前面調用Enter方法或其餘獲取鎖的方法,如lock,這點很重要。對應Enter方法,Monitor給出來另外一種實現TryEnter。這兩種方法的主要區別在因而否阻塞當前線程,Enter方法在獲取不到鎖時,會阻塞當前線程直到獲得鎖。不過缺點是若是永遠得不到鎖那麼程序就會進入死鎖狀態。咱們能夠採用Wait來解決,在調用Wait時加入超時時限就能夠。spa
if (Monitor.TryEnter(Names))
{
Monitor.Wait(Names, 1000); // !!
Names.Remove(name);
Console.WriteLine("Del: {0}", Names.Count);
Monitor.Pulse(Names);
} 線程