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

c#語言-多線程中的鎖系統(一)

編輯:C#入門知識

c#語言-多線程中的鎖系統(一)


介紹 平常在多線程開發中,總避免不了線程同步。本篇就對net多線程中的鎖系統做個簡單描述。   目錄 一:lock、Monitor        1:基礎。        2: 作用域。        3:字符串鎖。        4:monitor使用   二:mutex   三:Semaphore   四:總結   一:lock、Monitor 1:基礎   Lock是Monitor語法糖簡化寫法。Lock在IL會生成Monitor。          //======Example 1=====             string obj = "helloworld";             lock (obj)             {                 Console.WriteLine(obj);             }             //lock  IL會編譯成如下寫法             bool isGetLock = false;             Monitor.Enter(obj, ref isGetLock);             try             {                 Console.WriteLine(obj);             }             finally             {                 if (isGetLock)                 {                     Monitor.Exit(obj);                 }             }   isGetLock參數是Framework  4.0後新加的。 為了使程序在所有情況下都能夠確定,是否有必要釋放鎖。例: Monitor.Enter拿不到鎖   Monitor.Enter 是可以鎖值類型的。鎖時會裝箱成新對象。   2:作用域        一:Lock是只能在進程內鎖,不能跨進程,這個無需多說。        二:關於對type類型的鎖。如下:   復制代碼    //======Example 2=====             new Thread(new ThreadStart(() => {                 lock (typeof(int))                 {                     Thread.Sleep(10000);                     Console.WriteLine("Thread1釋放");                 }             })).Start();             Thread.Sleep(1000);             lock(typeof(int))             {                 Console.WriteLine("Thread2釋放");             }         我們在來看個例子。       //======Example 3=====             Console.WriteLine(DateTime.Now);             AppDomain appDomain1 = AppDomain.CreateDomain("AppDomain1");             LockTest Worker1 = (LockTest)appDomain1.CreateInstanceAndUnwrap(              Assembly.GetExecutingAssembly().FullName,              "ConsoleApplication1.LockTest");             Worker1.Run();               AppDomain appDomain2 = AppDomain.CreateDomain("AppDomain2");             LockTest Worker2 = (LockTest)appDomain2.CreateInstanceAndUnwrap(             Assembly.GetExecutingAssembly().FullName,             "ConsoleApplication1.LockTest");             Worker2.Run(); /// <summary>     /// 跨應用程序域邊界或遠程訪問時需要繼承MarshalByRefObject     /// </summary>     public class LockTest : MarshalByRefObject     {         public void Run()         {             lock (typeof(int))             {                 Thread.Sleep(10000);                 Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + ": Thread 釋放," + DateTime.Now);             }         }     }             第一個例子說明,在同進程同域,不同線程下,鎖type int,其實鎖的是同一個int對象。所以要慎用。   第二個例子,這裡就簡單說下。         A: CLR啟動時,會創建 系統域(System Domain)和共享域(Shared Domain), 默認程序域(Default AppDomain)。 系統域和共享域是單例的。程序域可以有多個,例子中我們使用AppDomain.CreateDomain方法創建的。         B:  按正常來說,每個程序域的代碼都是隔離,互不影響的。但對於一些基礎類型來說,每個程序域都重新加載一份,就顯得有點浪費,帶來額外的損耗壓力。聰明的CLR會把一些基本類型Object, ValueType, Array, Enum, String, and Delegate等所在的程序集MSCorLib.dll,在CLR啟動過程中都會加載到共享域。  每個程序域都會使用共享域的基礎類型實例。           C: 而每個程序域都有屬於自己的托管堆。托管堆中最重要的是GC heap和Loader heap。GC heap用於引用類型實例的存儲,生命周期管理和垃圾回收。Loader heap保存類型系統,如MethodTable,數據結構等,Loader heap生命周期不受GC管理,跟程序域卸載有關。        所以共享域中Loader heap MSCorLib.dll中的int實例會一直保留著,直到進程結束。單個程序域卸載也不受影響。作用域很大有沒有!!!        這時第二個例子也很容易理解了。 鎖int實例是跨程序域的,MSCorLib中的基礎類型都是這樣。 極容易造成死鎖,慎用。  而自定義類型則會加載到自己的程序域,不會影響別人。   3:字符串的鎖   我們都知道鎖的目的,是為了多線程下值被破壞。也知道string在c#是個特殊對象,值是不變的,每次變動都是一個新對象值,這也是推薦stringbuilder原因。如例:     //======Example 4=====         string str1 = "mushroom";         string str2 = "mushroom";         var result1 = object.ReferenceEquals(str1, str2);         var result2 = object.ReferenceEquals(str1, "mushroom");         Console.WriteLine(result1 + "-" + result2);         /* output          * True-True          */       正式由於c#中字符串的這種特性,所以字符串是在多線程下是不會被修改的,只讀的。它存在於SystemDomain域中managed heap中的一個hash table中。Key為string本身,Value為string對象的地址。    當程序域需要一個string的時候,CLR首先在這個Hashtable根據這個string的hash code試著找對應的Item。如果成功找到,則直接把對應的引用返回,否則就在SystemDomain對應的managed heap中創建該 string,並加入到hash table中,並把引用返回。所以說字符串的生命周期是基於整個進程的,也是跨AppDomain。   4:monitor用法   介紹下Wait,Pulse,PulseAll的用法。有注釋,大家直接看代碼吧。      static string str = "mushroom";         static void Main(string[] args)         {             new Thread(() =>             {                 bool isGetLock = false;                 Monitor.Enter(str, ref isGetLock);                 try                 {                     Console.WriteLine("Thread1第一次獲取鎖");                     Thread.Sleep(5000);                     Console.WriteLine("Thread1暫時釋放鎖,並等待其他線程釋放通知信號。");                     Monitor.Wait(str);                      Console.WriteLine("Thread1接到通知,第二次獲取鎖。");                     Thread.Sleep(1000);                 }                  finally                 {                     if (isGetLock)                     {                         Monitor.Exit(str);                         Console.WriteLine("Thread1釋放鎖");                     }                 }             }).Start();             Thread.Sleep(1000);             new Thread(() =>             {                 bool isGetLock = false;                 Monitor.Enter(str, ref isGetLock); //一直等待中,直到其他釋放。                 try                 {                     Console.WriteLine("Thread2獲得鎖");                     Thread.Sleep(5000);                     Monitor.Pulse(str); //通知隊列裡一個線程,改變鎖狀態。  Pulseall 通知所有的                     Console.WriteLine("Thread2通知其他線程,改變狀態。");                     Thread.Sleep(1000);                 }                 finally                 {                     if (isGetLock)                     {                         Monitor.Exit(str);                         Console.WriteLine("Thread2釋放鎖");                     }                 }               }).Start();             Console.ReadLine();     二:mutex  lock是不能跨進程鎖的。 mutex作用和lock類似,是能跨進程鎖的。 我們來看個例子         static bool createNew = false;         //第一個參數 是否應擁有互斥體的初始所屬權。即createNew true時,mutex默認獲得處理信號         //第二個是名字,第三個是否成功。         public static Mutex mutex = new Mutex(true, "mushroom.mutex", out createNew);           static void Main(string[] args)         {             //======Example 5=====             if (createNew)  //第一個創建成功,這時候已經拿到鎖了。 無需再WaitOne了。一定要注意。             {                 try                 {                     Run();                 }                 finally                 {                     mutex.ReleaseMutex(); //釋放當前鎖。                   }             }             //WaitOne 函數作用是阻止當前線程,直到拿到收到其他實例釋放的處理信號。             //第一個參數是等待超時時間,第二個是否退出上下文同步域。             else if (mutex.WaitOne(10000,false))//             {                 try                 {                     Run();                 }                 finally                 {                     mutex.ReleaseMutex();                 }             }             else//如果沒有發現處理信號             {                 Console.WriteLine("已經有實例了。");                 Console.ReadLine();             }         }         static void Run()         {             Console.WriteLine("實例1");             Console.ReadLine();         }       我們順序起A  B實例測試下。   A首先拿到鎖,輸出 實例1 。   B在等待, 如果10秒內A釋放,B拿到執行Run()。  超時後輸出  已經有實例了。   這裡注意的是第一個拿到處理信號 的實例,已經拿到鎖了。不需要再WaitOne。  否則報異常。         三:Semaphore  即信號量,我們可以把它理解為升級版的mutex。mutex對一個資源進行鎖,semaphore則是對多個資源進行加鎖。   semaphore內部一個線程計數器,線程每調用一次,計數器減一,釋放後對應加一。 超出線程數量則排隊等候。semaphore也是可以跨進程的。      static void Main(string[] args)         {             Console.WriteLine("准備處理隊列");               bool createNew = false;               SemaphoreSecurity ss = new SemaphoreSecurity(); //信號量權限控制             Semaphore semaphore = new Semaphore(2, 2, "mushroom.Semaphore", out createNew,null);             for (int i = 1; i <= 5; i++)             {                 new Thread((arg) =>                 {                     semaphore.WaitOne();                     Console.WriteLine(arg + "處理中");                     Thread.Sleep(10000);                     semaphore.Release(); //即semaphore.Release(1)                     //semaphore.Release(5);可以釋放多個,但不能超過最大值。如果最後釋放的總量超過本身總量,也會報錯。 不建議使用                   }).Start(i);             }             Console.ReadLine();         }       四:總結  mutex和Semaphore  性能較差,需要跨進程的時候,再使用。    lock和Monitor    性能較好些。    注意死鎖。

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