背景:使用者領取優惠券,同一個使用者需要加鎖驗證是否已經領取,不同使用者則可以同時領取。
上程式碼示例:
1、建立Person類
/// <summary> /// Person類 /// </summary> public class Person { /// <summary> /// id /// </summary> public int Id { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 是否獲得優惠券 /// </summary> public bool IsGetCoupon { get; set; } }
2.1、不加鎖的方法(可能會出現重複領取的情況)
/// <summary> /// 獲取優惠券 /// </summary> public static void GetCoupon(Person person) { Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},前來領取優惠券", DateTime.Now, person.Name); if (person.IsGetCoupon) { //假裝業務處理 Thread.Sleep(1000); Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},已經領取,不可重複領取", DateTime.Now, person.Name); } else { //假裝業務處理 Thread.Sleep(1000); //領取 person.IsGetCoupon = true; Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},領取成功", DateTime.Now, person.Name); } }
2.2、加lock鎖的方法,所有來領優惠券的人,都得排對領(也不好)
/// <summary> /// Lock獲取優惠券 /// </summary> public static void LockGetCoupon(Person person) { Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},前來領取優惠券", DateTime.Now, person.Name); lock (LockObj) { //判斷是否已經領取 if (person.IsGetCoupon) { //假裝業務處理 Thread.Sleep(1000); Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},已經領取,不可重複領取", DateTime.Now, person.Name); } else { //假裝業務處理 Thread.Sleep(1000); //領取 person.IsGetCoupon = true; Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},領取成功", DateTime.Now, person.Name); } } }
2.3、mutex鎖,互斥鎖,只有相同id的人,才會排對領取,不同id的人就可以同時領取
/// <summary> /// Mutex,領取 /// </summary> /// <param name="person"></param> public static void MutexGetCoupon(Person person) { Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},前來領取優惠券", DateTime.Now, person.Name); using (var mutex = new Mutex(false, person.Id.ToString())) { try { if (mutex.WaitOne(-1, false)) { //判斷是否已經領取 if (person.IsGetCoupon) { //假裝業務處理 Thread.Sleep(1000); Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},已經領取,不可重複領取", DateTime.Now, person.Name); } else { //假裝業務處理 Thread.Sleep(1000); //領取 person.IsGetCoupon = true; Console.WriteLine("date:{0:yyyy-MM-dd HH:mm:ss},name:{1},領取成功", DateTime.Now, person.Name); } } } catch (Exception ex) { //TxtLogHelper.WriteLog(ex); } finally { mutex.ReleaseMutex(); } } } }
3.1、開始測試(不加鎖)
static void Main(string[] args) { //例項化三個人 Person p1 = new Person { Id = 24, Name = "Kobe" }; Person p2 = new Person { Id = 25, Name = "Rose" }; Person p3 = new Person { Id = 23, Name = "Lebl" }; //開啟多執行緒、模擬三個人同時發起多次領取請求 for (int i = 0; i < 4; i++) { new Thread(() => { GetCoupon(p1); }).Start(); new Thread(() => { GetCoupon(p2); }).Start(); new Thread(() => { GetCoupon(p3); }).Start(); } Console.ReadLine(); }
測試結果:每個人都重複領取
3.2、測試lock鎖方法,
private static readonly object LockObj = new object(); static void Main(string[] args) { //例項化三個人 Person p1 = new Person { Id = 24, Name = "Kobe" }; Person p2 = new Person { Id = 25, Name = "Rose" }; Person p3 = new Person { Id = 23, Name = "Lebl" }; //開啟多執行緒、模擬三個人同時發起多次領取請求 for (int i = 0; i < 4; i++) { new Thread(() => { LockGetCoupon(p1); }).Start(); new Thread(() => { LockGetCoupon(p2); }).Start(); new Thread(() => { LockGetCoupon(p3); }).Start(); } Console.ReadLine(); }
測試結果:雖然避免了重複領取,但是每個人都的每個請求都要排對。如果使用者量大的話,這種方式效率就太低了,所以不推薦。
3.3、測試mutex鎖,互斥鎖
static void Main(string[] args) { //例項化三個人 Person p1 = new Person { Id = 24, Name = "Kobe" }; Person p2 = new Person { Id = 25, Name = "Rose" }; Person p3 = new Person { Id = 23, Name = "Lebl" }; //開啟多執行緒、模擬三個人同時發起多次領取請求 for (int i = 0; i < 4; i++) { new Thread(() => { MutexGetCoupon(p1); }).Start(); new Thread(() => { MutexGetCoupon(p2); }).Start(); new Thread(() => { MutexGetCoupon(p3); }).Start(); } Console.ReadLine(); }
測試結果:既避免了重複領取,也避免了堵塞使用者請求的情況。見下面截圖,Kobe、Rose、Lebl是同時領取的優惠券,但是每個人的重複請求都在排對
總結:mutex鎖,完美的解決了此類問題。