上一章講了基元線程同步構造,而其它的線程同步構造都是基於這些基元線程同步構造的,並且一般都合並了用戶模式和內核模式構造,我們稱之為混合線程同步構造。
在沒有線程競爭時,混合線程提供了基於用戶模式構造所具備的性能優勢,而多個線程競爭一個構造時,混合線程通過基元內核模式的構造來提供不“自旋”的優勢。
那麼接下來就是個簡單的混合線程同步構造的例子,可與上一章最後的那些例子相比較:
public class SimpleHybridLock : IDisposable { private Int32 m_waiters = 0; private AutoResetEvent m_waiterlock = new AutoResetEvent(false);//注意這裡是false public void Enter() { if (Interlocked.Increment(ref m_waiters)==1) { return; } m_waiterlock.WaitOne(); } public void Leave() { if (Interlocked.Decrement(ref m_waiters) == 0) { return; } m_waiterlock.Set(); } public void Dispose() { m_waiterlock.Dispose(); } }
上面的例子學了上一張後看起來感覺很簡單就不講解了,只是一個簡單的,將Interlocked這種互鎖構造和自動重置事件構造AutoResetEvent 結合起來的,混合線程同步構造的例子。
上面混合鎖可以去加入自旋,當超過一定的自旋次數時再進行阻塞。也可以去加入互斥體的遞歸玩法,總之這個東西充滿了無限的可能。
.NET 框架類庫中的混合構造
總體而言,實際上就是對上面那個簡單例子的擴展,它們的目的都是為了使線程能盡可能不去進入內核模式,並且減少線程競爭時自旋的性能影響。
雖然提供了這麼多同步構造,且玩法也很多。但是最重要的還是一點:能盡量避免就避免阻塞線程,否則應盡量使用Volatile和Interlocked方法,因為它們速度快,然而這兩個只能操作簡單類型。
一定要阻塞,就可以使用Monitor類,也可以用ReaderWriterLockSlim類,雖然比Monitor慢,但是允許多個線程並發進行,提升了總體性能,減少阻塞線程的幾率。
用System.Lazy類或者System.Threading.LazyInitializer類去替代雙檢索玩法。
一句話解決這個點:
Lazy<String> s=new Lazy<String>(()=>DateTime.Now.ToLongTimeString(),true);
調用的話就用s.Value,實際上就是封裝了雙檢索,有些地方加了些優化。目的就是延時加載。
異步鎖
其實叫異步的同步構造,因為一般的同步構造都是用阻塞線程或者自旋來完成,而異步鎖的目的就是為了不阻塞來玩。
SemaphoreSlim類的WaitAsync方法就是這個思路,信號量玩法而已。
而reader-writer語義的玩法是ConcurrentExclusiveSchedulerPair類。(當沒有ConcurrentScheduler任務時,使用ExclusiveScheduler為獨占式運行。沒有ExclusiveScheduler運行時,ConcurrentScheduler調度的任務可同時進行)
並發集合類
FCL自帶四個線程安全的集合類,全在System.Collections.Concurrent(Concurrent為並發的意思)命名空間中定義。
它們是ConcurrentQueue,ConcurrentStack,ComcurrentDictionary和ConcurrentBag。
所有這些都是“非阻塞“的。(實際上在ConcurrentQueue,ConcurrentStack和ConcurrentBag為空的時候還要提取數據,那麼提取數據的這個線程就會被阻塞)