第三十章 混合線程同步構造

混合線程同步構造:合併了用戶模式和內核模式構造。沒有線程競爭時,混合構造提供了基元用戶模式構造所具備的性能優點。多個線程競爭一個構造時,混合構造經過基元內核模式的構造來提供不「自旋」的優點。數據結構

30.1 一個簡單的混合鎖

internal sealed class SimpleHybirdLock : IDisposable
{
private Int32 m_waiters = 0; private AutoResetEvent m_waiterLock = new AutoResetEvent(false); public void Enter() { if(Interlockd.Increment(ref m_waiters )== 1) return; m_waiterLock.Wati } public void Leave() { if(Interlickd.Decrement(ref m_waiters) == 0 ) return; m_waiterLcok.Set(); } public void Dispose() { m_waiterLock.Dispose(); } }

30.2 自旋,線程全部權和遞歸

 因爲轉換爲內核模式會形成巨大的性能損失,並且線程佔有鎖的時間一般都很短,因此爲了提高應用程序的整體性能,可讓一個線程在用戶模式中「自旋」一小段時間,再讓線程轉換爲內核模式。若是線程正在等待的鎖在線程「自旋」期間變得可用,就能避免向內核模式的轉換了。此外,有的鎖限制只能由得到所得線程釋放鎖。有的鎖容許當前擁有它的線程遞歸地擁有鎖。併發

internal public sealed class AnotherHybridLock : IDisposable 
{ private Int32 m_waiters = 0;
   private AutoResetEvent m_waiterLock = new AutoResetEvent(false);
   private Int32 m_spincout = 4000;
   private Int32 m_owingThreadId = 0, m_recursion = 0;
   public void Enter()
{
   Int32 threadId = Thread.CurrentThread.ManagedThreadId;
     if(threadId == m_owingThreadId){
        m_recursion++; return;
     }          
     SpinWait spinwait = new SpinWait();
  } }

30.3 FCL中的混合構造

 30.3.1 ManualResetEventSlim類和SemaphoreSilm類異步

兩個構造的工做方式和對應的內核模式構造徹底一致,只是它們都在用戶模式中「自旋」,並且都推遲到發生第一次競爭時,才建立內核模式的構造。性能

30.3.2 Monitor類和同步塊spa

 堆中的每一個對象均可關聯一個名爲同步塊的數據結構。同步塊包含字段。它爲內核對象,擁有線程的ID,遞歸計數以及等待線程計數提供了相應的字段。線程

問題總結:代理

變量能引用一個代理對象-前提是變量引用的那個對象的類型派生自System.MarshalByRefObject類。code

若是線程調用Monitor.Enter,向它傳遞對類型對象的引用,並且這個類型對象是以「AppDomain中立」的方式加載的,線程就會跨越進程中的全部AppDomain在那個類型上獲取鎖。對象

因爲字符串能夠留用,因此兩個徹底獨立的代碼段可能在不知情的狀況下獲取對內存的一個String對象引用。blog

跨越AppDomain邊界傳遞字符串時,CLR不建立字符串的副本;相反,它只是將對字符串的一個引用傳給其餘AppDomain。

因爲Monitor的方法要獲取一個Object,因此傳遞類型會致使類型被裝箱,形成現場在以裝箱對象上獲取鎖。

向方法應用【MethodImpl(MethodImplOption.Synchronized)】特性,會形成JIT編譯器用Monitor.Enter和Monitor.Exit調用包圍方法的本機代碼。

調用類型的類型構造器時,CLR要獲取類型對象上的一個鎖,確保只有一個線程初始化類型對象及其靜態字段。

30.3.3 ReaderWriterLockSilm類

 ReaderWriterLockSilm:

一個線程向數據寫入時,請求訪問的其餘全部線程都被阻塞。

一個線程從數據讀取時,請求的讀取的其餘線程容許繼續執行,但請求寫入的線程仍被阻塞。

向線程寫入的線程結束後,要麼解除一個寫入線程的阻塞,使它能向數據寫入,要麼解除全部讀取線程的阻塞,使它能併發讀取數據。若是沒有線程被阻塞,鎖就進入能夠自由使用的狀態,可供下一個reader或writer線程獲取。

從數據讀取的全部線程結束後,一個writer線程被解除阻塞,使它能向數據寫入,若是沒有線程被阻塞,鎖就進入能夠自由使用的狀態,可供下一個reader或writer線程獲取。

30.3.4 OneManyLock類 

30.3.5 CountdownEvent類

 這個構造使用一個ManualResetEventSlim對象。這個構造阻塞一個線程,知道它的內部計數變成0。和Semaphore的行爲相反。

30.3.6 Barrier類 

30.3.7 線程同步構造小結

   代碼儘可能不要阻塞任何線程。執行異步計算或I/O操做時,將數據從一個線程交給另外一個線程時,應避免多個線程同時訪問數據。若是不能徹底作到這一點,請儘可能使用Volatile和Interlocked的方法。

   如下兩種狀況阻塞線程。

  線程模型很簡單。

  線程有專門用途。

30.4 著名的雙檢鎖技術

 開發人員用「雙檢鎖」將單實例對象的構造推遲到應用程序首次請求該對象時進行。也稱「延遲初始化」。

30.5 條件變量模式

 

30.6 異步的同步構造

 

30.7 併發集合類

相關文章
相關標籤/搜索