51

C# 针对特定的条件进行锁操作,不用lock,而是mutex - pig-tong

 4 years ago
source link: https://www.cnblogs.com/bookobe/p/11229021.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C# 针对特定的条件进行锁操作,不用lock,而是mutex

背景:用户领取优惠券,同一个用户需要加锁验证是否已经领取,不同用户则可以同时领取。

上代码示例:

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();
        }

测试结果:每个人都重复领取

830428-20190722223920905-1376624974.png

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();
        }

测试结果:虽然避免了重复领取,但是每个人都的每个请求都要排对。如果用户量大的话,这种方式效率就太低了,所以不推荐。

830428-20190722223719512-1023261365.png

 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是同时领取的优惠券,但是每个人的重复请求都在排对

830428-20190722224356979-1645531747.png

总结:mutex锁,完美的解决了此类问题。

 --------------------------------------------华丽的分割线 --------------------------------------------

感谢各位大佬提出的问题和建议,我确实没有考虑到这些问题。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK