本文只針對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); }