C# 針對特定的條件進行鎖操作,不用lock,而是mutex

tong發表於2019-07-22

背景:使用者領取優惠券,同一個使用者需要加鎖驗證是否已經領取,不同使用者則可以同時領取。

上程式碼示例:

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鎖,完美的解決了此類問題。

 

相關文章