【转】衔接UI线程和管理后台工作线程的类(多线程、异步调用)

衔接UI线程和管理后台工作线程的类(多线程、异步调用)

转:http://www.cnblogs.com/net66/archive/2005/08/03/206132.html

一、引言
     在编写Windows form时,如果直接在UI线程要运行一个费时方法的话(如从数据库查询大量数据时),会引起程序“假死”,从而导致用户不满。这个时候就需要通过多线程技术来解决,提高界面交互性能,方便用户使用。
一般通过三种方式解决:
1.通过System.Threading.Thread类,创建新的线程,Thread.Start运行费时方法。
2.通过System.Threading.ThreadPool类,将费时任务提交到线程池中,等待运行。
以上两种方法,基本思路是在UI界面中控制线程的启动和中止,在线程中回调用UI界面方法,更新界面。在线程中回调UI界面方法时,特别是涉及更新控件属性时,如果不注意,存在很大的隐患。这两种办法,编码和控制结构较为复杂,需要启动和管理额外的线程占用资源。
3.通过异步委托调用,将该方法排队到系统线程池的线程中运行,而在费时方法中也通过Control.BeginInvoke异步回调,达到"启动后不管"的目的。
这种方法,编码简单,程序结构较为清晰,充分利用.NET框架的异步委托功能,但要对异步调用知识较熟悉。
相关知识点参见
     现利用.NET异步委托调用功能,编写Task抽象类,以方便管理后台工作线程,衔接后台线程与UI线程的联系。该抽象类提供了调用和管理的框架,没有方法的实现细节,通过继承类、重写方法,可以实现想要的功能。主要功能如下:
1.利用异步委托调用,实际多线程,不需要单独后台线程。
2.通过委托、事件驱动,实际后台与前台UI线程的联系,实现事件广播。
3.支持正常取消后台工作方法(费时方法)运行,也可以强制中止线程。
4.能够捕获取消、强制中止和方法出错三种情况,并突发相关事件,以便进行释放资源等操作。
5.通过异步调用,在工作方法中安全调用涉及UI控件的方法。
6.自行管理工作进程状态,提供状态变化事件。
7.只要工作方法调用签名,符合定义的TaskDelegate委托接口,可通过StartTask(TaskDelegate worker ,params object[] args )方便调用。在实际使用时,可在继承类中定义多个相同调用接口的方法,避免重复编码,较为方便。

给大家作个参考,而大牛呢,多点指正。当是扔个砖头,想砸块玉吧。


二、代码

  1using System;
  2using System.Windows.Forms;
  3
  4namespace Net66.AsynchThread
  5{
  6    /// <summary>
  7    /// 任务工作状态
  8    /// </summary>

  9    public enum TaskStatus 
 10     
 37
 38    /// <summary>
 39    /// 任务状态消息
 40    /// </summary>

 41    public class TaskEventArgs : EventArgs 
 42         
137
138    /// <summary>
139    /// 任务的工作方法(Work)的委托接口
140    /// 传入值:对象数组(object[])
141    /// 返回值:对象(object)
142    /// </summary>

143    public delegate object TaskDelegate( params object[] args ); 
144
145    /// <summary>
146    /// 任务事件的委托接口
147    /// </summary>

148    public delegate void TaskEventHandler( object sender, TaskEventArgs e ); 
149
150    abstract public class Task
151    {   
152        内部属性
178
179        事件
201
202        属性
267
268        触发事件
358
359        工作进程管理
497
498        工作方法的基础
520    }

521}

522
523使用Task类
636



三、示例
1.启动时的UI界面


2.后台工作方法(费用方法)运行后,任务状态为Running


3.强制中止工作方法,运行任务状态Aborted


4.工作方法突发错误时,任务状态ThrowErrorStoped


5.工作方法正常结束或正常取消而结束时,任务状态Stopped


示例代码下载

posted on 2005-08-03 00:19 Net66 阅读(5867) 评论(27)  编辑 收藏 所属分类: C#

评论

#1楼  2005-08-03 00:50 idior      

good. 多线程的文章比较少, 下次谈谈同步的问题吧.
    回复  引用  查看    

#2楼  2005-08-03 01:50 木野狐      

学习。
    回复  引用  查看    

#3楼 218.81.148.* 2005-08-03 07:11 gogo [未注册用户]

很好,收藏研究.
    回复  引用    

#4楼 218.249.96.* 2005-08-03 08:46 Nineteen@newsmth [未注册用户]

System.Threading.ThreadPool还是不要用的好,这东西牵扯进程的异步调用,这东西微软就该设置成internal
    回复  引用    

#6楼  2005-08-03 08:54 James      

这些BeginXXXX的方法都是使用了ThreadPool的。
    回复  引用  查看    

#7楼 222.47.87.* 2005-08-03 09:06 张老三 [未注册用户]

刚完成了一个图库管理系统, 使用的就是这种方式. 感觉还不错.
    回复  引用    

#8楼 218.79.240.* 2005-08-03 09:08 win [未注册用户]

文章的意思应该是不用显式使用ThreadPool,通过系统默认调用吧
    回复  引用    

#9楼  2005-08-03 09:23 BearTang      

好文章。。
    回复  引用  查看    

#10楼  2005-08-03 09:39 妖居      

异步委托也是刚刚用过,做一个分析目录的工具的时候用的。
    回复  引用  查看    

#11楼  2005-08-03 09:41 James      

我的意思是说,这些异步委托调用什么的,其内部都是使用了ThreadPool的,这样当然不用你自己去直接使用ThreadPool了。
    回复  引用  查看    

#12楼  2005-08-03 18:48 yinh      

最近写的项目中因为用得很多网络交互,频繁的异步调用,但对控制这些线程却没有一点主意,希望能从你的文章中得到一些启示。
    回复  引用  查看    

#13楼  2005-09-12 18:34 沧海一声笑      

研究中、。。。
太好了 还有源码,可以少走一些弯路了
谢谢!
    回复  引用  查看    

#14楼  2005-09-14 16:10 双鱼座      

看了下楼主的代码,大致上不错。不过在封装性方面做得不够...其实封装性是需要早一些构思的,比功能的实现还要再早一些。
先搞清楚你的Task的派生类依赖什么,也就是说可供派生类调用的方法,例如Fire***方法;接下来搞清楚你的Task的派生类不依赖什么,例如必须在重写方法中调用base.Work这样的问题;最后是参数的传递问题。由此我想到了在Delphi3中对Thread的封装,有一个同步执行方法的方法,在调用者不知道任何细节的情况下可以进行安全的调用。当然,那个封装代价有点大了。

我作了如下修改:
1.定义一个抽象方法:
protected abstract object Execute(params object [] args);
这个方法才是真正需要继承的方法。
2.基类的Work方法改成私有方法(Work这个名字取得不是太好),代码这样写:
System.Threading.Thread.CurrentThread.IsBackground = true;
_workThread = System.Threading.Thread.CurrentThread;
return Execute(args);
3.加一个线程方法:
void start()
{
StartTask(new TaskDelegate(Work), tempArgs);
}
4.定义一个私有字段用来向线程传递参数:
private object[] tempArgs;
5.最后加一个启动方法:
public void Start(params object[] args)
{
tempArgs = args;
Thread thread = new Thread(new ThreadStart(start));
thread.Start();
}

改造完成了。客户端代码由此变得非常简洁:
1.当按下“开始执行”时的代码:
_Task.Start(new object[] {});
2.Concrete类型newasynchui的代码也简单了,只需要重写一个Execute方法而不是重载一个Work和另外写一个Work2,在方法中也不必调用base.Work,你想调用也调用不了,因为是那是私有方法。

当然还有其它一些更好的建议,例如不必定义那么多的事件,其实一两个就足够了,也不需要派生新的EventArgs类型,因为从sender中可以获得所有的信息。

我的解决方案可能更邪一点了,因为我是用反射和Emit实现的,原理和你的差不多,只不过客户端代码可以非常简单而已。
    回复  引用  查看    

#15楼 218.81.143.* 2005-09-14 20:59 net66      

谢谢,[双鱼座]评论.说得非常到位,很有价值:).
我想你的解决方案一定相当不错,能否Mail一份,学习探讨探讨.the8341 at gmail.com
    回复  引用  查看    

#16楼  2005-09-19 11:19 双鱼座      

@net66:
呵呵,OK,这两天我整理一下也写篇文章和你分享。
    回复  引用  查看    

#17楼 218.79.244.* 2005-09-19 11:36 net66      

@双鱼座
:-) 好文共享,期待中
    回复  引用  查看    

#18楼  2005-09-19 17:35 双鱼座      

    回复  引用  查看    

#19楼 [楼主] 2005-09-20 09:10 Net66      

双鱼座的文章,作者功底颇深.
http://www.cnblogs.com/Barton131420/articles/239855.html
    回复  引用  查看    

#20楼 222.82.56.* 2006-10-07 16:37 可乐[匿名] [未注册用户]

看了例程非常激动,也许是还有很多东西要学没有看的很彻底,希望楼主能解释,就是在UI中启动一个辅助线程后往往会应为需要在UI启动的这个辅助线程中在启动其他线程,这个时候该怎样使用Task抽象类,该如何将辅助线程启动的其他线程中的信息显示在UI中,还有怎么才能利用AsynchThread 在UI中停止辅助线程的时候能同时停止辅助线程启动的其他线程,希望楼主能解答,谢谢!!
    回复  引用    

#21楼 58.241.234.* 2007-05-04 15:07 小雨 [未注册用户]

文章写的不错,狂学习。
    回复  引用    

#22楼  2007-07-19 13:33 yellowyu      

谢谢楼主与双鱼座,我还有点问题,想清楚了再跟你们沟通
    回复  引用  查看    

#23楼  2008-01-10 10:56 蓝天旭日      

非常经典!值得研究!
    回复  引用  查看    

#24楼  2008-03-21 21:45 程之恒      

谢谢,收藏了!
    回复  引用  查看    

#25楼 222.20.100.* 2008-08-19 17:54 娄玲 [未注册用户]

下了,研究研究,谢谢啦。
    回复  引用    
posted @ 2008-09-26 00:32  赖文华.NET  阅读(308)  评论(0编辑  收藏  举报