[知识背景]
所谓自定义通知事件,就是指在自己的类中定义的事件,该事件用于向调用者发出通知。比如做一个下载工具,下载是需要时间的,用户在界面里点击“下载”之后,我的下载类在后台开启线程开始传输数据,前台界面上可以同时执行其他操作。当数据传输完成,需要通知界面(调用者)已完成下载,以便界面上做相应的改变。这就需要在我的下载类中有类似 DownloadCompleted 的事件,这样在用户的代码中可以通过 downloader.DownloadCompleted += new new EventHandler(XXXXX) 进入他自己的事件处理函数。
这里说的跨线程问题,是指非法的跨线程调用问题。还用上个例子,在下载完成时,需要改变界面中 Label 控件的 Text 属性以提示用户下载完成。这就牵涉到在另一个类所创建的线程中操纵UI线程中创建的控件。这种做法在 .NET 中是不推荐的,同时这样会严重影响代码质量。(可参考MSDN:ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxmclictl/html/138f38b6-1099-4fd5-910c-390b41cbad35.htm)
[案例]
自定义一个类似与 Timer 的控件,并实现一个 Tick 事件。调用者在 Tick 事件里操纵界面上的元素。
代码段:
Form1.cs 里的代码:
2 {
3 InitializeComponent();
4 MyTimer mytimer = new MyTimer();
5 mytimer.Tick += new EventHandler(mytimer_Tick);
6 mytimer.Start();
7 }
8
9 void mytimer_Tick(object sender, EventArgs e)
10 {
11
12 CheckBox check = new CheckBox();
13 check.Top = this.Controls.Count * check.Height;
14 this.Controls.Add(check);
15 }
MyTimer.cs 文件里的代码:
protected virtual void OnTick(EventArgs e)
{
if (Tick != null)
{
Tick(this, e);
}
}
public void Start()
{
if (_bRunning)
{
return;
}
_threadWork = new Thread(new ThreadStart(ThreadWork));
_threadWork.IsBackground = true;
_threadWork.Start();
}
public void Stop()
{
_threadWork.Abort();
_threadWork = null;
}
private void ThreadWork()
{
while (true)
{
OnTick(new EventArgs());
Thread.Sleep(_interval);
}
}
这种写法是MSDN里常用的一种,但是那里没有牵涉到跨线程调用。在这个案例下,这种做法就行不通了。调试时会报错。如图。
分析:
这里的 OnTick 事件是在Start()方法中创建的线程中调用的,在 OnTick 中调用了 Tick,由委托机制可知,等同于调用了 Form1.cs 中的 mytimer_Tick ,而mytimer_Tick 调用了 UI 上的东西,这时在 mytimer_Tick 中就会出现跨线程调用。
解决办法:
改写 OnTick 方法。使用 Invoke 机制来完成调用。
1 protected virtual void OnTick(EventArgs e)
3 if (Tick != null)
4 {
5 if (Tick.Target is System.ComponentModel.ISynchronizeInvoke)
6 {
7 System.ComponentModel.ISynchronizeInvoke aSynch = Tick.Target as System.ComponentModel.ISynchronizeInvoke;
8 if (aSynch.InvokeRequired)
9 {
10 object[] args = new object[2] {this, e };
11 aSynch.BeginInvoke(Tick, args);
12 }
13 else
14 {
15 Tick(this, e);
16 }
17 }
18 }
19 }
源代码下载:测试自定义通知事件的跨线程问题.rar