前言: 在系統中多少會遇到某些交易間需要互斥(不然容易造成DeadLock).
在我們當前系統中有許多動作間需要互斥,不然會有DeadLock問題
藉由已經分析DeadLock Report後,我開始構思如何讓建立Lock可以變得更容易且好理解.
所以就建構出此Lock架構.
如何在此框架使用Lock機制 我們只需要做幾個步驟:
在使用Lock類別上掛LockerInterceptor攔截器標籤.   
使用Lock方法上使用LockAttribute標籤[Lock(LockKey = "Your lock Key")](Key屬性是必填的) 
設定Lock屬性. 
 
目前寫法是針對單一Server Mutiple Thread來建立互斥Lock. 假如有遇到多台Servers需要建立互斥模式可以,參考Redis的Redlock.Net.
 
使用方法如下,這樣在多執行緒系統中MethodA1跟MethodB_A就不會有同時執行問題,這樣就可以造成這兩個動作互斥.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [Intercept(typeof(LockerInterceptor)) ] public  class  LockerContext  : ILockerContext {     [Lock(Key = "A" ) ]     public  virtual  void  MethodA1 ()     {         Thread.Sleep(5 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodA1 Done" );         Thread.Sleep(5 );     }     [Lock(Key = "A" ) ]     public  virtual  void  MethodB_A ()     {         Thread.Sleep(5 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodB_A Done" );         Thread.Sleep(5 );     } } 
 
架構解說 我是使用ReaderWriterLockSlim 
因為支援ReadLock不互斥,WriteLock互斥邏輯
 
我是如何讓使用者輸入Key來建立不同lock呢?
我是使用ConcurrentDictionary 來處理此問題,每個Key都有不同Lock物件
 
LockAttribute LockAttribute有幾個屬性.
Key:鎖名稱 
Mode:鎖的模式
LockMode.XLock:獨占鎖會排斥其他資源請求此鎖,須等待資源釋放. 
LockMode.Shared:Shared lock之間不互斥. 
 
 
Order:因為支援多個LockAttribute,此屬性決定執行此方法前要求鎖順序 
 
1 2 3 4 5 6 7 8 9 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true) ] public  sealed  class  LockAttribute  : Attribute {     public  string  Key { get ; set ; }     public  LockMode Mode { get ; set ; } = LockMode.XLock;     public  int  Order { get ; set ; } } 
 
LockerInterceptor 我們直接來IInterceptor物件最核心邏輯方法Intercept(IInvocation invocation).
利用GetCustomAttributes取得方法上所有LockAttribute並在方法執行前要求拿到,所需要Lock資源才可以執行方法,最後在finally時釋放lock資源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public  void  Intercept (IInvocation invocation ){     var  methodName = invocation.Method.Name;     var  lockAttributes = invocation.Method.GetCustomAttributes(typeof (LockAttribute), true ) as  LockAttribute[];     if  (IsMarkLockLockAttribute(lockAttributes))     {         var  lockProviders = GetLockProviders(lockAttributes);         try          {             foreach  (var  lockProvider in  lockProviders)             {                 lockProvider.AddLock();             }             invocation.Proceed();         }         catch  (Exception e)         {             _log.Exception("Something wrong!" , e);             throw  e;         }         finally  {             foreach  (var  lockProvider in  lockProviders)             {                 lockProvider.ReleaseLock();             }             _log.Info($"{DateTime.Now:HH:mm:ss fff}  {methodName}  Release Lock" );         }     }     else      {         invocation.Proceed();     }      } 
 
SampleCode 我利用Nunit寫一個簡單程式Print在console讓我們方便觀看結果.
MethodA:Shared lock mode on key A 
MethodA1:X lock mode on key A 
MethodB_A:X lock mode on key A and B 
MethodB:X lock mode on key B 
 
理論上MethodB只會對於MethodB_A互斥,MethodB並不會跟MethodA,MethodA1有互斥反應.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 [Intercept(typeof(LockerInterceptor)) ] public  class  LockerContext  : ILockerContext {     [Lock(Key = "A" , Mode = LockMode.SharedLock) ]     public  virtual  void  MethodA  ()     {         Thread.Sleep(100 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodA Done" );         Thread.Sleep(100 );     }     [Lock(Key = "A" ) ]     public  virtual  void  MethodA1 ()     {         Thread.Sleep(100 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodA1 Done" );         Thread.Sleep(100 );     }     [Lock(Key = "A" ) ]     [Lock(Key = "B" ) ]     public  virtual  void  MethodB_A ()     {         Thread.Sleep(100 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodB_A Done" );         Thread.Sleep(100 );     }     [Lock(Key = "B" ) ]     public  virtual   void  MethodB ()     {         Thread.Sleep(100 );         Console.WriteLine($"{DateTime.Now:HH:mm:ss fff}  MethodB Done" );         Thread.Sleep(100 );     } } public  interface  ILockerContext {     void  MethodA () ;     void  MethodA1 () ;          void  MethodB_A () ;          void  MethodB () ; } public  class  AutofacConfig {     public  static  IContainer Container { get ; set ; }     public  static  void  Register ()     {         ContainerBuilder builder = new  ContainerBuilder();         builder.RegisterType<LockerInterceptor>().AsSelf();         builder.RegisterType<LockerContext>().As<ILockerContext>().EnableClassInterceptors();         builder.RegisterType<ConsoleProvider>().As<ISysLog>().SingleInstance();         Container = builder.Build();     } } [TestFixture ] public  class  LockerTest {          [OneTimeSetUp ]     public  void  OneTimeSetUp ()     {         AutofacConfig.Register();     }     [Test ]     public  void  LockGroupTest ()     {          var  lockerContext= AutofacConfig.Container.Resolve<ILockerContext>();                  List<Task> taskList =new  List<Task>();         for  (int  i = 0 ; i < 10 ; i++)         {             taskList.Add(Task.Factory.StartNew(() => { lockerContext.MethodA(); }));             taskList.Add(Task.Factory.StartNew(() => { lockerContext.MethodA1(); }));             taskList.Add(Task.Factory.StartNew(() => { lockerContext.MethodB_A(); }));                             taskList.Add(Task.Factory.StartNew(() => { lockerContext.MethodB(); }));         }         Task.WaitAll(taskList.ToArray());     } } 
 
Result 
我們發現MethodB_A這個方法會對於,所有key是A,B方法互斥,MethodA則不會對於MethodA,MethodB互斥.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 14:26:59 815 MethodA1 Done 14:26:59 816 MethodB Done 14:26:59 919 MethodA1 Release Lock 14:26:59 920 MethodB Release Lock 14:27:00 020 MethodB Done 14:27:00 120 MethodB Release Lock 14:27:00 220 MethodB_A Done 14:27:00 320 MethodB_A Release Lock 14:27:00 420 MethodA1 Done 14:27:00 520 MethodA1 Release Lock 14:27:00 620 MethodB_A Done 14:27:00 720 MethodB_A Release Lock 14:27:00 820 MethodB Done 14:27:00 820 MethodA1 Done 14:27:00 920 MethodB Release Lock 14:27:00 920 MethodA1 Release Lock 14:27:01 020 MethodB_A Done 14:27:01 120 MethodB_A Release Lock 14:27:01 220 MethodB_A Done 14:27:01 320 MethodB_A Release Lock 14:27:01 420 MethodB Done 14:27:01 420 MethodA1 Done 14:27:01 520 MethodB Release Lock 14:27:01 520 MethodA1 Release Lock 14:27:01 620 MethodA Done 14:27:01 620 MethodA Done 14:27:01 620 MethodA Done 14:27:01 620 MethodA Done 14:27:01 620 MethodA Done 14:27:01 720 MethodA Release Lock 14:27:01 720 MethodA Release Lock 14:27:01 720 MethodA Release Lock 14:27:01 720 MethodA Release Lock 14:27:01 720 MethodA Release Lock 14:27:01 820 MethodB Done 14:27:01 920 MethodB Release Lock 14:27:02 020 MethodB Done 14:27:02 120 MethodB Release Lock 14:27:02 220 MethodB_A Done 14:27:02 320 MethodB_A Release Lock 14:27:02 420 MethodA1 Done 14:27:02 520 MethodA1 Release Lock 14:27:02 620 MethodB_A Done 14:27:02 720 MethodB_A Release Lock 14:27:02 820 MethodB_A Done 14:27:02 920 MethodB_A Release Lock 14:27:03 020 MethodA1 Done 14:27:03 020 MethodB Done 14:27:03 120 MethodA1 Release Lock 14:27:03 120 MethodB Release Lock 14:27:03 220 MethodA1 Done 14:27:03 220 MethodB Done 14:27:03 320 MethodA1 Release Lock 14:27:03 320 MethodB Release Lock 14:27:03 420 MethodB_A Done 14:27:03 520 MethodB_A Release Lock 14:27:03 620 MethodA1 Done 14:27:03 720 MethodA1 Release Lock 14:27:03 820 MethodB_A Done 14:27:03 920 MethodB_A Release Lock 14:27:04 020 MethodA1 Done 14:27:04 020 MethodB Done 14:27:04 120 MethodA1 Release Lock 14:27:04 120 MethodB Release Lock 14:27:04 220 MethodA Done 14:27:04 220 MethodA Done 14:27:04 220 MethodA Done 14:27:04 220 MethodA Done 14:27:04 320 MethodA Release Lock 14:27:04 320 MethodA Release Lock 14:27:04 320 MethodA Release Lock 14:27:04 320 MethodA Release Lock 14:27:04 420 MethodB Done 14:27:04 520 MethodB Release Lock 14:27:04 620 MethodB_A Done 14:27:04 720 MethodB_A Release Lock 14:27:04 820 MethodA1 Done 14:27:04 920 MethodA1 Release Lock 14:27:05 020 MethodA Done 14:27:05 120 MethodA Release Lock 
 
小結 本篇主要想要介紹使用Lock機制.
利用ReaderWriterLockSlim 就可以建立如DB lock,實在非常方便.
假如想要細部了解Autofac + Interceptors(AOP) 動態代理 可以參考我之前寫文章,這裡我就不多敘述了.
SourceCode LockService 
__此文作者__:Daniel Shih(石頭) __此文地址__: https://isdaniel.github.io/AOP-Lock-Mechanism/   __版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW  許可協議。轉載請註明出處!