写在前面的一些废话:
原本是在学习GDI+的,在动手做一个图像处理的小程序时突发奇想,想把很费时的图象处理代码放在一个后台线程中,放在后台线程中后又想把进度通过进度条显示出来,所以就学习了一些多线程方面的知识,发现.net提供了很方便的方法
,呵呵,方便啊方便,真方便。。。
===================废话好了,正文开始====================
首先一个概念:
1、UI线程:
大部分的Windows窗体应用程序最终都只有一个线程,所有的UI活动都发生在这个线程上,这个线程通常称为UI线程。
一句话就是拥有窗体,按钮等等的那个线程。
2、异步委托调用:
.net为我们提供了一个很简单的多线程编程方法,即利用异步委托。调用委托的BeginInvoke方法,我们既可以让一些较耗时的操作在系统线程池的线程中运行,这样就不会阻塞UI线程而导致程序界面没有响应了。这种方式比较简单,我们举一个简单的例子:
//我们先定义一个委托
public delegate void SlowWork_delegate(myclass myArgs);
public void SlowWork(myclass args)

...{
//do some slow work
//这个函数将在另一个线程中运行
}
//在这个函数中,我们将调用SlowWork函数
//并让它在另一个线程中运行
//这个线程不需要我们维护。
public void callSlowWork()

...{
//SlowWork函数需要一个myclass类型的参数,我们先创建一个
myclass mc = new myclass();
//创建一个委托
SlowWork_delegate d = new SlowWork_delegate(SlowWork);

//异步调用这个委托,clr会从托管的线程池中获取一个线程来运行SlowWork函数
//mc是SlowWork需要的参数,如果需要传多个参数的话我们可以自定义一个结构
d.BeginInvoke(mc,null,null);
}

这就是一个多线程程序了,简单吧。。。。
但是这个例子比较有局限性,有时我们想知道这个在后台工作的线程工作的怎么样了,最常见的比如说我们在一个后台线程中处理一幅图,我们需要把处理情况反映在一个进度条上,但是我们不能在后台线程中去更新由UI线程创建的控件。比如在上个例子中,我们直接在SlowWork函数中去更新进度条的话会抛出异常的
public void SlowWork(myclass args)

...{
//do some slow work
//这个函数将在另一个线程中运行
ProgressBar1.Value = aInt; //会产生无法预料的后果
}
这种情况我们可以利用Control类提供的BeginInvoke方法来实现,看下面的例子:
注意:这里用了一个.net 2.0的新东西:EventHandler泛型委托,说穿了就是由.net预先定义好的一个delegate
只要我们写的函数的签名符合他的类型就能直接用EventHandler了,而不用我们自己定义一个委托了
//先定义一个我们自己的EventArgs类来传递我们需要的数据
public class myEventArgs : EventArgs

...{
private int _percent;
Public myEventArgs(int val)

...{
percent = val;
}
public int percent

...{

get...{ return _percent; }

set...{ _percent = value; }
}
}
//这个函数用来更新UI
public void updateUI(object o,myEventArgs args)

...{
ProgressBar1.Value = args.percent;
}

//下面这个函数是在另一个线程中运行,所以不能直接调用updateUI函数
public void SlowWork(myclass args)

...{
//这个函数将在另一个线程中运行
//do some slow work
//ProgressBar1.Value = aInt; //会产生无法预料的后果
//updateUI(null,new myEventArgs(aInt));//显然这样调用和上面的没啥区别

//这里才是区别
//创建一个updateUI函数的委托
EventHandler ehandle = new EventHandler(updateUI);

//调用Control类的BeginInvoke方法,这里假设我们是在form里写的代码
//因为form是继承自Control类的,所以这里就直接调用BeginInvoke方法了

BeginInvoke(ehandle,new object[]...{null,new myEventArgs(aInt)});
}

public void callSlowWork()

...{
//SlowWork函数需要一个myclass类型的参数,我们先创建一个
myclass mc = new myclass();
//创建一个委托
SlowWork_delegate d = new SlowWork_delegate(SlowWork);

//异步调用这个委托,clr会从托管的线程池中获取一个线程来运行SlowWork函数
//mc是SlowWork需要的参数,如果需要传多个参数的话我们可以自定义一个结构
d.BeginInvoke(mc,null,null);
}

我们可以再修改一下上面的updateUI函数,使其调用更加方便
//这个函数用来更新UI
public void updateUI(object o,myEventArgs args)

...{
//如果是由其他线程调用的,就采取异步方式
if(ProgressBar1.InvokeRequired)

...{
EventHandler eh = new EventHandle(updateUI);

BeginInvoke(eh,new object[]...{0,args});
}
else //如果不是其他线程调用的,就直接更新
ProgressBar1.Value = args.percent;
}
这样,在SlowWork函数中直接调用updateUI函数就可以了,不需要创建委托。
上面已经是一段相对比较完整的代码了,一般的多线程任务都能用以上的方法来解决了^^
有时间我会上传一个完整的程序上来
参考资料:http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/misMultithreading.mspx?mfr=true
发表于 @ 2006年12月21日 15:38:00|评论(loading...)|编辑