[筆記]C# 鎖定-使用lock、Monitor.Enter、Monitor.TryEnter的小範例

在C#中使用lock、Monitor.Enter、Monitor.TryEnter的筆記

使用情境

例如常遇到的資料庫要取最後一筆編號+1當流水號,在多人同時使用系統的情況下

容易發生流水號重複的問題,

使用lock

    class Program
    {

        public static object lockobj = new object();
        static void Main(string[] args)
        {
            Program p = new Program();
            var tt1 = new ParameterizedThreadStart(p.DoWrite);
            Thread t1 = new Thread(tt1);


            var tt2 = new ParameterizedThreadStart(p.DoWrite);
            Thread t2 = new Thread(tt2);

            t2.Start(2);
            t1.Start(1);

        }
        public  void DoWrite(object o)
        {
            lock(lockobj)
            {
                
                Console.WriteLine("start-"+o);
                Thread.Sleep(3000);
                Console.WriteLine("done-" + o);
            }
        }

    }

執行結果:

因為先執行t2.Start(2); 再執行t1.Start(1); lock的情況下會先執行的跑完 才會執行下一個

如果把lock拿掉的話

public  void DoWrite(object o)
        {
            //lock(lockobj)
            //{
                Console.WriteLine("start-"+o);
                Thread.Sleep(3000);
                Console.WriteLine("done-" + o);
            //}
        }

執行結果就會變成

使用Monitor.Enter

    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            var tt3 = new ParameterizedThreadStart(p.MDoWrite);
            Thread t3 = new Thread(tt3);
            t3.Start(3);

            var tt4 = new ParameterizedThreadStart(p.MDoWrite);
            Thread t4 = new Thread(tt4);
            t4.Start(4);
        }

        public void MDoWrite(object o)
        {
            Monitor.Enter(this);
            try
            {
                Console.WriteLine("start-" + o);
                Thread.Sleep(3000);
                
            }
            finally
            {
                Console.WriteLine("done-" + o);
                Monitor.Exit(this);
            }

        }
    }

執行結果:

使用Monitor.TryEnter

在使用lock時要小心鎖死的問題

Monitor提供了TryEnter方法可以傳入等待逾時時間

class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();

            var tt5 = new ParameterizedThreadStart(p.M2DoWrite);
            Thread t5 = new Thread(tt5);
            t5.Start(5);

            var tt6 = new ParameterizedThreadStart(p.M2DoWrite);
            Thread t6 = new Thread(tt5);
            t6.Start(6);
        }
        public void M2DoWrite(object o)
        {
            Monitor.TryEnter(this,6000);
            try
            {
                Console.WriteLine("start-" + o);
                Thread.Sleep(5000);
                Console.WriteLine(o+"-完成" );
            }
            finally
            {
                Console.WriteLine("done-" + o);
                Monitor.Exit(this);
            }
            Console.WriteLine("要跳出了");
        }

    }

執行結果:

假設超過傳入的等待逾時時間

public void M2DoWrite(object o)
        {
            Monitor.TryEnter(this,2000);
            try
            {
                Console.WriteLine("start-" + o);
                Thread.Sleep(5000);
                Console.WriteLine(o+"-完成" );
            }
            finally
            {
                Console.WriteLine("done-" + o);
                Monitor.Exit(this);
            }
            Console.WriteLine("要跳出了");
        }

會發現t5 還沒結束 t6就Start了

而且還會吐Error

接著我把程式修改成這樣
 

public void M2DoWrite(object o)
        {
            bool NotOverTime = Monitor.TryEnter(this,2000);

            try
            {
                Console.WriteLine("進入時間第{0}秒",DateTime.Now.Second);
                Console.WriteLine("start-" + o);
                Thread.Sleep(5000);
                Console.WriteLine(o + "-完成");
            }
            finally
            {
                Console.WriteLine("done-" + o);
                if (NotOverTime)
                {
                    Monitor.Exit(this);
                }
            }
            Console.WriteLine(o + "-要跳出了");
        }

執行結果: