程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 多線程中的鎖系統(三)-WaitHandle、AutoResetEvent、ManualResetEvent,manualresetevent

多線程中的鎖系統(三)-WaitHandle、AutoResetEvent、ManualResetEvent,manualresetevent

編輯:C#入門知識

多線程中的鎖系統(三)-WaitHandle、AutoResetEvent、ManualResetEvent,manualresetevent


 

介紹

     本章主要說下基於內核模式構造的線程同步方式,事件,信號量。

目錄

一:理論

二:WaitHandle

三:AutoResetEvent

四:ManualResetEvent

五:總結

 

一:理論

我們曉得線程同步可分為,用戶模式構造和內核模式構造。

內核模式構造:是由windows系統本身使用,內核對象進行調度協助的。內核對象是系統地址空間中的一個內存塊,由系統創建維護。

  內核對象為內核所擁有,而不為進程所擁有,所以不同進程可以訪問同一個內核對象, 如進程,線程,作業,事件,文件,信號量,互斥量等都是內核對象。

  而信號量,互斥量,事件是windows專門用來幫助我們進行線程同步的內核對象。

  對於線程同步操作來說,內核對象只有2個狀態, 觸發(終止,true)、未觸發(非終止,false)。 未觸發不可調度,觸發可調度。

 

用戶模式構造:是由特殊CPU指令來協調線程,上節講的volatile實現就是一種,Interlocked也是。  也可稱為非阻塞線程同步。

 

二:WaitHandle

在windows編程中,我們通過API創建一個內核對象後會返回一個句柄,句柄則是每個進程句柄表的索引,而後可以拿到內核對象的指針、掩碼、標示等。

 而WaitHandle抽象基類類作用是包裝了一個windows內核對象的句柄。我們來看下其中一個WaitOne的函數源碼(略精簡)。

        public virtual bool WaitOne(TimeSpan timeout)
        {
            return WaitOne(timeout, false); 
        }

        [System.Security.SecuritySafeCritical]  // auto-generated
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
        private bool WaitOne(long timeout, bool exitContext)
        {
            return InternalWaitOne(safeWaitHandle, timeout, hasThreadAffinity, exitContext);
        }
        [System.Security.SecurityCritical]  
        internal static bool InternalWaitOne(SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
        {
            Contract.EndContractBlock();
            int ret = WaitOneNative(waitableSafeHandle, (uint)millisecondsTimeout, hasThreadAffinity, exitContext);
            
            if (ret == WAIT_ABANDONED)
            {
                ThrowAbandonedMutexException();
            }
            return (ret != WaitTimeout);
        }
        //調用win32 waitforsingleobjectEx
        [System.Security.SecurityCritical] 
        [ResourceExposure(ResourceScope.None)]
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);

 

WaitAll 和WaitAny 調用win32中,waitformultipleobjectsEx函數。 

SignalAndWaitOne 調用win32中,signalandwait函數。

調用api帶ex都是設置超時的。 如果我們在c#中不傳,默認是-1 表示無限期等待。

其中SafeWaitHandle字段,包含了一個win32內核對象句柄。

理解了WaitHandle其他都好辦了,我們來看下它的派生類型。

WaitHandle
  |——EventWaitHandle                  事件構造。
    |——AutoResetEvent
    |——ManualResetEvent
  |——Semaphore                         信號量構造。
  |——Mutex

其中Semaphore和mutex第一章已經說過了,下面來看看其他的。

 

三:AutoResetEvent

   使用示例如下,有簡單注釋。   關於描述,盡量貼近系統自身術語。

  static void Main(string[] args)
        {
            //AutoResetEvent example
            //AutoResetEvent 通知正在等待的線程已發生的事件。
            AutoResetEvent waitHandler = new AutoResetEvent(false);//false 即非終止,未觸發。
            new Thread(() =>
            {
                waitHandler.WaitOne();  //阻塞當前線程,等待底層內核對象收到信號。
                Console.WriteLine("接收到信號,開始處理。");

            }).Start();
            new Thread(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("發信號");
                waitHandler.Set();    //向內核對象發送信號。設置事件對象為非終止狀態、false,解除阻塞。  

            }).Start();
            //waitHandler.Close(); //釋放句柄資源。
            //waitHandler.Reset();  //手動設置事件為非終止狀態、false,線程阻止。
            Console.ReadLine();
        }

 

WaitOne 阻塞線程,非自旋。

Set()   發出一個信號後,設置事件狀態為false。  這本應該是2步的操作,AutoResetEvent.set()函數,給2步一起自動做了,很方便。

 

四:ManualResetEvent

 這個和上面基本一樣,從字面來說需要手動重置狀態,我們來看例子。

            ManualResetEvent manualWaitHandler = new ManualResetEvent(false);//false 即非終止,未觸發。
            new Thread(() =>
            {
                manualWaitHandler.WaitOne();  //阻塞當前線程對象,等待信號。
                Console.WriteLine("接收到信號,開始處理。");

                manualWaitHandler.Reset();  //手動 設置事件對象狀態為非終止狀態,false。
                manualWaitHandler.WaitOne();  //這裡直接阻塞等待無效,因為事件對象還是true,必須手動調reset。
                Console.WriteLine("第二次接收到信號,開始處理。");

            }).Start();
            new Thread(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("發信號");
                manualWaitHandler.Set();    //向事件對象發送ok信號。。

                Thread.Sleep(2000);
                Console.WriteLine("第二次發信號");
                manualWaitHandler.Set();
            }).Start();
            Console.ReadLine();

 

這2則區別很小,其實是系統Api的區分,不是net類庫實現的。

在Win32Native類中,我可以看到KERNEL32 api 有這麼個參數isManualReset。

 [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
        [ResourceExposure(ResourceScope.Machine)] // Machine or none based on the value of "name"
        internal static extern SafeWaitHandle CreateEvent(SECURITY_ATTRIBUTES lpSecurityAttributes, bool isManualReset, bool initialState, String name);

 

五:總結

基於內核模式構造的同步步驟是,   托管代碼->用戶模式代碼->內核模式代碼,反之一樣。 這樣一路下來,性能肯定好不到那裡。大家注意區分。

用戶模式構造, 是利用CPU特殊指令,進行原子操作。

用戶模式代碼,如圖。 是指  托管代碼調用 win32代碼 這一層,   之後在調內核模式代碼。 

 

參考資源

1:CLR via c#

2:windows核心編程第五版

 

如有錯誤之處,歡迎指出糾正。  對您有幫助的,請推薦下n(*≧▽≦*)n。

作者:蘑菇先生      

出處:http://www.cnblogs.com/mushroom/p/4198429.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved