浅谈 .Net 中 lock 与 Monitor 之锁定方法同步操作

需求:

最近在项目中遇到一个情况,某一个方法需要完成一个号码预占的动作,并发量也比较大。

并且要求第一个提交抢号的人得到该号码。而后其他并发提交的人提示抢号失败。

废话不多说,上代码。

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                Thread thread = new Thread(new ThreadStart(new Preemption().test));
                thread.Start();
                Thread.Sleep(300);//请求间隔
            }
        }
    }

    public class Preemption : Base
    {
        public void test()
        {
            base.LockTest("400-6666-8888");
        }
    }

    public class Base
    {
        /// <summary>
        /// 预占号码列表
        /// </summary>
        private static List<string> numberArr = new List<string>();

        public bool LockTest(string number)
        {
            if (numberArr.Contains(number))
            {
                Console.WriteLine(number + "已被锁定");
                return false;
            }
            else
            {
                try
                {
                    numberArr.Add(number);

                    Console.WriteLine("已锁定" + number);

                    int index = numberArr.FindIndex(t => t == number);

                    lock (numberArr[index])
                    {
                        Console.WriteLine(number + "正在预占...");

                        Thread.Sleep(3000);//模拟该方法操作为3秒

                        Console.WriteLine(number + "预占成功!");
                    }
                }
                catch (Exception)
                {
                }
                finally
                {
                    numberArr.Remove(number); //执行完成后在预占列表中清除该号码
                }
                return true;
            }
        }
    }
lock版多并发预占号码

运行的效果图如下:

     

     并发预占间隔为300毫秒一次,预占号码消耗时间为3秒一次。由以上代码改变这2个参数可得到不同的效果。

---------------------------------------------【Monitor版】----------------------------------------------------

同样的需求,下面来一个Monitor版本的。

代码如下:

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                Thread thread1 = new Thread(new ThreadStart(new Preemption().test));
                thread1.Start();
                Thread.Sleep(1000);//并发时间
            }
        }
    }

    public class Preemption : Base
    {
        public void test()
        {
            new Base().LockTest("400-6666-8888");
        }
    }

    public class Base
    {
        /// <summary>
        /// 预占号码列表
        /// </summary>
        private static List<string> numberArr = new List<string>();

        public void LockTest(string number)
        {
            if (numberArr.Contains(number))
            {
                Console.WriteLine(number + "已被锁定");
            }
            else
            {
                numberArr.Add(number);

                int index = numberArr.FindIndex(t => t == number);

                if (Monitor.TryEnter(numberArr[index], 250))//250毫秒的等待
                {
                    try
                    {
                        Console.WriteLine("已锁定" + number);

                        Console.WriteLine(number + "正在预占...");

                        Thread.Sleep(5000);//操作时间

                        Console.WriteLine(number + "预占成功!");
                    }
                    finally
                    {
                        Monitor.Exit(numberArr[index]);//释放锁

                        numberArr.Remove(number); //执行完成后在预占列表中清除该号码
                    }
                }
                else
                {
                    Console.WriteLine(number + "已被锁定");
                }
            }
        }
    }
Monitor版多并发预占号码

运行效果如下:

 

并发预占间隔为1秒一次,预占号码等待时长为250毫秒。由以上代码改变这2个参数可得到不同的效果。

 

总结:

     以上操作总体为多用户操作并发时,根据条件在某种情况下对不同实例对象同一方法的请求,将只有第一条请求生效,其他后续并发无效。

     如果该号码正在预占,就返回失败。

     在Monitor版中加入了等待时间,更好的规避了死锁。

     且以上两种锁定的只是LockTest方法,其他方法都不会被干预。

原文地址:https://www.cnblogs.com/PandaMars/p/3305419.html