最近客户反映系统老是时不时的卡死一次,开始觉得是电脑环境问题,后来发现其余电脑上也会出现这种问题。收到反馈后,就开始找缘由呀 ,重现呀。折腾了一上午终于找到了缘由:“死锁”;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: }