您的位置:首页 > 产品设计 > UI/UE

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

2008-09-26 00:32 435 查看
衔接UI线程和管理后台工作线程的类(多线程、异步调用)

转:/article/6213742.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

523

三、示例

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

回复 引用

#5楼 222.43.70.* 2005-08-03 08:49 学习 [未注册用户]

http://www.vckbase.com/document/viewdoc/?id=1126

回复 引用

#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 双鱼座

我的文章已经写完:http://Barton131420.cnblogs.com/articles/239855.html

回复 引用 查看

#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 娄玲 [未注册用户]

下了,研究研究,谢谢啦。

回复 引用

td { font-size: 12px }
.commentTextBox
{
font-family : Verdana;
font-size: 13px;
}
a.blue:visited,
a.blue:active,
a.blue:link,
a.blue:hover
{
color:blue;
}

.userData { BEHAVIOR: url(#default#userdata) }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐