c#中多線程同步Lock(鎖)的研究以及跨線程UI的操作


本文只針對C#中,多線程同步所用到的鎖(lock)作為研究對象。由於想更直觀的顯示結果,所以,在做demo的時候,就把多線程通過事件操作UI的代碼也寫了出來,留作備忘和分享吧。

 

其實多線程的同步,使用同步鎖的方法用了好多次,今天無意中看到MSDN中,建議用:

1 private static readonly object locker1 = new object();
2 private readonly object locker2 = new object();

備注:原文並沒有加readonly,是我后來自己加進去的。

我不僅思考了一下他們的區別。

然后我寫了一段代碼進行測試,測試類代碼如下:

    /// <summary>
    /// 跨線程操作UI的時候傳遞的參數,本文為了顯示消息,所以簡單的封裝了一個
    /// </summary>
    public class MyEventArgs : EventArgs
    {
        public readonly string Message = string.Empty;
        public MyEventArgs(string msg)
        {
            this.Message = msg;
        }
    }
    /// <summary>
    /// 測試類,用於測試2種鎖的區別
    /// </summary>
    public class LockTest
    {
        //2個鎖
        private static readonly object Locker1 = new object();
        private readonly object Locker2 = new object();

        /// <summary>
        /// 跨線程操作UI的委托和事件
        /// </summary>
        public delegate void MessageEventHandler(object sender, MyEventArgs e);
        public event MessageEventHandler MessageEvent;
        public void OnMessage(MyEventArgs e)
        {
            if (this.MessageEvent != null) MessageEvent(this, e);
        }

        //要鎖的變量,通過它可以看出2種鎖在不同情況下的效果
        private int num = 0;
        //實例名字
        private readonly string Name;
        public LockTest(string name)
        {
            Name = name;
        }
        //第一種鎖執行的方法
        public void AddNum1()
        {
            lock (Locker1)
            {
                num = 0;
                ShowMessage();
            }
        }
        //第二種鎖執行的方法
        public void AddNum2()
        {
            lock (Locker2)
            {
                num = 0;
                ShowMessage();
            }
        }
        //鎖內的一些操作,並通過事件,把關鍵的消息顯示到主線程中的UI里
        private void ShowMessage()
        {
            string msg = "";
            for (int i = 0; i < 10; i++)
            {
                num += 1;
                msg = string.Format("線程 [{0}],實例[{1}]中num的值是[{2}]", Thread.CurrentThread.Name, this.Name, num);
                OnMessage(new MyEventArgs(msg));
                Thread.Sleep(100);
            }
            msg = string.Format("======線程 [{0}]執行完畢======", Thread.CurrentThread.Name);
            OnMessage(new MyEventArgs(msg));
        }
    }

測試用的類寫完了,開始測試:

首先測試單個實例、多線程,2種鎖的區別:

        private void button1_Click(object sender, EventArgs e)
        {
            LockTest test = new LockTest("LockTest 1");
            test.MessageEvent += new LockTest.MessageEventHandler(MessageCallBack);
            listBox1.Items.Clear();
            for (int i = 0; i <= 2; i++)
            {
                Thread a = new Thread(new ThreadStart(test.AddNum1));
                a.Name = i.ToString();
                a.Start();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            LockTest test = new LockTest("LockTest 1");
            test.MessageEvent += new LockTest.MessageEventHandler(MessageCallBack);
            listBox1.Items.Clear();
            for (int i = 0; i <= 2; i++)
            {
                Thread a = new Thread(new ThreadStart(test.AddNum2));
                a.Name = i.ToString();
                a.Start();
            }
        }

  輸出結果一模一樣:

得出結論:如果對一個實例,多線程訪問的時候,2種鎖是沒有區別的。

下面是測試多個實例的情況(靜態鎖):

        private void button3_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
            for (int i = 0; i <= 2; i++)
            {
                LockTest test = new LockTest("LockTest " + i.ToString());
                test.MessageEvent += new LockTest.MessageEventHandler(MessageCallBack);
                Thread a = new Thread(new ThreadStart(test.AddNum1));
                a.Name = i.ToString();
                a.Start();
            }
        }

得到結果:

得出結論,在靜態鎖面前,線程依舊要排隊,雖然不是一個實例,但是鎖是唯一的,線程只認鎖,所以線程並沒有並發!

繼續測試(非靜態的鎖):

        private void button4_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();
            for (int i = 0; i <= 2; i++)
            {
                LockTest test = new LockTest("LockTest " + i.ToString());
                test.MessageEvent += new LockTest.MessageEventHandler(MessageCallBack);
                Thread a = new Thread(new ThreadStart(test.AddNum2));
                a.Name = i.ToString();
                a.Start();
            }
        }

得到的結果:

得出結論:非靜態鎖的時候,多線程並發了,一起在工作。

 

其實,測試的結果之前也能猜想出來,只不過,不測試下,心里總是覺得沒底,呵呵,測試完了,也就徹底釋然了!

 

窗體中,用於事件回調,顯示到UI里的代碼在這里:

delegate void MessageHandler(string msg);
        public void MessageCallBack(object sender, MyEventArgs e)
        {
            MessageHandler handler = new MessageHandler(ShowMessage);
            this.Invoke(handler, new object[] { e.Message });
        }

        public void ShowMessage(string msg)
        {
            this.listBox1.Items.Add(msg);
        }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM