本篇我們主要總結和介紹一下利用屬性標簽方式對多線程進行方法同步和上下文同步,主要用到的是MethodImplAttribute 類 和 SynchronizationAttribute 類。
這兩個屬於方法特性和類的特性,標識某個方法或類是同步方法,本質上還是基於Lock的實現的。
首先我們還是來看一個例子,假如小明和小紅兩個人用都是主附銀行卡,兩個人都可以對帳戶進行操作,比如帳戶余額1000元,比如兩個人幾乎同時進行取錢600元的操作。
代碼如下:我們沒有進行任何的同步措施,我們運行一下代碼,看一下結果:(不同計算機執行結果可能不同)
namespace ThreadAttribute { class Program { static void Main(string[] args) { Account account = new Account(); Thread thread1 = new Thread(new ParameterizedThreadStart(account.WithDraw)); thread1.Name = "小明"; Thread thread2 = new Thread(new ParameterizedThreadStart(account.WithDraw)); thread2.Name = "小紅"; thread1.Start(600); thread2.Start(600); Console.ReadKey(); } } public class Account { private static int _balance; public int Blance { get { return _balance; } } public Account() { _balance = 1000; } public void WithDraw(object money) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", Thread.CurrentThread.Name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", Thread.CurrentThread.Name); } } } }
這結果顯然是不對的,我們來對程序進行修改。我們先對MethodImplAttribute 類進行簡單介紹:
在使用 MethodImplAttribute 類之前需要引用 System.Runtime.Remoting.Contexts 命名空間,System.Runtime.Remoting.Contexts 命名空間包含的一些屬性將影響CLR 在運行期間的行為,MethodImplAttribute 就是這樣一個屬性,MethodImplAttribute類的一個構造函數把MethodImplOptions枚舉作為其參數,MethodImplOptions 枚舉有一個字段Synchronized,,它指定在任一時刻只允許一個線程訪問這個方法。
既然這樣,我們對程序這樣修改,直接在 WithDraw() 方法增加 MethodImplAttribute 標簽,代碼如下:
[MethodImplAttribute(MethodImplOptions.Synchronized)] public void WithDraw(object money) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", Thread.CurrentThread.Name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", Thread.CurrentThread.Name); } }
我們再次運行程序,結果如下圖:
上面已經提到,這個兩個類其實都是基於Lock關鍵字實現的,上面的代碼就和下面直接用Lock 是一樣的。
public void WithDraw(object money) { lock (this) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", Thread.CurrentThread.Name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", Thread.CurrentThread.Name); } } }
需要注意的是:
[MethodImplAttribute(MethodImplOptions.Synchronized)]標簽應用到實例方法,相當於對當前實例加鎖 lock(this)
[MethodImplAttribute(MethodImplOptions.Synchronized)]標簽應用到靜態方法,相當於當前類型加鎖。如 WithDraw 是靜態方法,就相當於 lock (typeof(Account))
接下來我們再來看看SynchronizationAttribute類:
MSDN對SynchronizationAttribute的解釋為:為當前上下文和所有共享同一實例的上下文強制一個同步域。
SynchronizationAttribute 的類:一個在 System.Runtime.Remoting.Contexts 命名空間中,另一個在 System.EnterpriseServices 命名空間中。System.EnterpriseServices.SynchronizationAttribute 類僅支持同步調用,並且只可與接受服務的組件一起使用。System.Runtime.Remoting.Contexts.SynchronizationAttribute 同時支持同步調用和異步調用,並且只可與上下文綁定對象一起使用。
至於什么應用程序域,什么是上下文,請看這兩篇文章
既然是SynchronizationAttribute 是類標簽屬性,那么我們需要對程序這樣修改:
首先注釋掉我們代碼中的 [MethodImplAttribute(MethodImplOptions.Synchronized)] 方法標簽並引用System.Runtime.Remoting.Contexts 命名空間,然后使 Account 類繼承 ContextBoundObject,並為其增加 SynchronizationAttribute標簽屬性[Synchronization(SynchronizationAttribute.REQUIRED, true)]即可,代碼如下:
[Synchronization(SynchronizationAttribute.REQUIRED, true)] public class Account : ContextBoundObject { private static int _balance; public int Blance { get { return _balance; } } public Account() { _balance = 1000; } public void WithDraw(object money) { if ((int)money <= _balance) { Thread.Sleep(2000); _balance = _balance - (int)money; Console.WriteLine("{0} 取錢成功!余額={1}", Thread.CurrentThread.Name, _balance); } else { Console.WriteLine("{0} 取錢失敗!余額不足!", Thread.CurrentThread.Name); } } }
執行結果如下: