最近客戶反映系統老是時不時的卡死一次,開始覺得是電腦環境問題,後來發現其餘電腦上也會出現這種問題。收到反饋後,就開始找緣由呀 ,重現呀。折騰了一上午終於找到了緣由:「死鎖」;css
這個死鎖發生的有點奇怪。由於只有一個同步根,怎麼也會死鎖呢。下面寫一段小代碼 描述一下發生死鎖的情形:html
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Data;
5: using System.Drawing;
6: using System.Linq;
7: using System.Text;
8: using System.Windows.Forms;
9: using System.Threading;
10:
11: namespace DeadLock
12: {
13: public partial class Form1 : Form
14: {
15: Button btnGetValue;
16: public Form1()
17: {
18: InitializeComponent();
19: this.Controls.Add(btnGetValue);
20: btnGetValue.Click += new EventHandler(btnGetValue_Click);
21: DataPoolManager.GetInstance().ValueChanged += new DataPoolManager.ValueChangedHandle(Form1_ValueChanged);
22: }
23:
24: void btnGetValue_Click(object sender, EventArgs e)
25: {
26: int index = 0;
27: string colName = string.Empty;
28: //do something....
29: DataPoolManager.GetInstance().GetValue(index, colName);
30: }
31:
32: void Form1_ValueChanged(object sender, EventArgs e)
33: {
34: //do something...
35: Thread.Sleep(100);
36: //形成死鎖的位置
37: this.Invoke((MethodInvoker)delegate()
38: {
39: //do something
40: });
41: }
42: }
43:
44: public class DataPoolManager
45: {
46: private DataPoolManager() { }
47:
48: static DataPoolManager instance;
49:
50: public static DataPoolManager GetInstance()
51: {
52: if (instance == null)
53: {
54: lock (syncRoot)
55: {
56: if (instance == null)
57: {
58: instance = new DataPoolManager();
59: }
60: }
61: }
62: return instance;
63: }
64:
65: /// <summary>
66: /// 同步根
67: /// </summary>
68: static readonly object syncRoot = new object();
69:
70: public delegate void ValueChangedHandle(object sender,EventArgs e);
71:
72: /// <summary>
73: /// 肇事事件 ,該事件被UI窗體註冊
74: /// </summary>
75: public event ValueChangedHandle ValueChanged;
76:
77: /// <summary>
78: /// 這個方法可能被UI線程調用
79: /// </summary>
80: public string GetValue(int rowIndex, string colName)
81: {
82: //形成死鎖的位置
83: lock (syncRoot)
84: {
85: //do something...
86: Thread.Sleep(100);
87: return DateTime.Now.ToString("yyyy-MM-dd HH:MM ss");
88: }
89: }
90:
91: /// <summary>
92: /// 這個方法由線程池中的某個線程調用,是個比較耗時的操做
93: /// </summary>
94: public bool SetValue(string key, string colName, string newValue)
95: {
96: bool result = false;
97: lock (syncRoot)
98: {
99: // do something...
100: Thread.Sleep(100);
101: if (result == true)
102: {
103: //引起肇事事件
104: OnValueChanged(new EventArgs());
105: }
106: }
107: return result;
108: }
109:
110: private void OnValueChanged(EventArgs eventArgs)
111: {
112: ValueChangedHandle handle = ValueChanged;
113: if (handle != null)
114: {
115: handle(this, eventArgs);
116: }
117: }
118:
119: }
120: }
在窗體上註冊了 DataPoolManager 的 ValueChanged 事件 ,該事件不是由UI線程引起的,因此在事件處理中 使用了 this.Invoke 方法 ,見代碼第37行。這本沒什麼錯誤,可是關鍵在於 這個事件是在 DataPoolManager 對象中 SetValue 方法中的 Lock 語句塊中引起的 。這就意味着若是該事件不執行完,同步根將一直被「佔用」。這時候 ,在SetValue方法沒有引起事件以前 ,若是UI線程調用了 GetValue 方法 ,UI線程將在代碼的 第 83 行出等待 ,而 執行 SetValue的方法引起事件後,在 代碼 37 行出 執行 this.Invoke 的時候 發生死鎖。this
找到緣由了,問題就好解決了,有兩個解決方案:spa
1,SetValue 方法中,引起事件的操做放到Lock塊的外部執行。以下:線程
1: public bool SetValue(string key, string colName, string newValue)
2: {
3: bool result = false;
4: lock (syncRoot)
5: {
6: // do something...
7: Thread.Sleep(100);
8: }
9: if (result == true)
10: {
11: //在同步塊外部引起事件
12: OnValueChanged(new EventArgs());
13: }
14: return result;
15: }
2,將事件處理中的this.Invoke 改爲 this.BeginInvoke:code
1: void Form1_ValueChanged(object sender, EventArgs e)
2: {
3: //do something...
4: Thread.Sleep(100);
5: //不等待返回
6: this.BeginInvoke((MethodInvoker)delegate()
7: {
8: //do something
9: });
10: }