不定时的间隔线程和线程排队执行(LumaQQ.NET异步线程处理)
2011-02-19 21:42
826 查看
在我的这篇文章《.NET Framework中的计时器对象
》中,讨论了几种.NET Framework定时器异同之处,以及使用场合。我当时的初忠也是在为LumaQQ.NET
寻
找一种最合理的线程定时执行和线程排队的方案。因为在LumaQQ中,有需要定时执行的线程,比如发送KeepLive包;有需要不同间隔时间,间隔时间
是动态的且不应该是“可重入”(前一个执行还没有完成,又再次进行执行定时任务),比如重发包;有需要在一个独立线程上不定期执行的任务,比如收到包后的
事件处理(收到数据包后,程序主线程需要继续去监听数据,而不能在主线程上去进行包的处理工作,这样如果事件代理的执行时间很长的话,主线程的执行周期被
占用了,而造成无法及时接收和发送数据包)。在这三种情况下,直接使用Timer组件肯定无法完全满足我们的要求。
通过分析Timer组件,我们就很容易发现,他们都是通过ThreadPool
这个类的功能来实现定时器的功能。ThreadPool类,提供了对当前进程内的后台线程统一管理,集中调配和资源重用的线程池实现。利用ThreadPool可以实现任务的延迟处理,I/0 异常操作,线程间的互斥执行,定时任务等功能。
ThreadPool提供了一系列管理和维护线程池的接口。在LumaQQ.NET中,我需要的功能就是利用:QueueUserWorkItem 和RegisterWaitForSingleObject 这两个接口来实现的。
QueueUserWorkItem :让我们可以注册一个排队的异步任务,等到线程池有剩余的线程资源时去异步执行任务。
RegisterWaitForSingleObject
:让我们可以注册一个等待信号就绪的异步任务,等到该线程等待的信号就绪后异步执行该任务。这个接口等待信号是一个WaitHandle对象,同时还可以
指定等待超时(间隔时间),而通过设置executeOnlyOnce参数的值可以指定是否重复执行该任务。
了解了ThreadPool的基本情况后,我们就可以来设计我们的异步线程执行框架。首先对异步任务和间隔任务,我们分别定义了两个接口ICallable
和IRunable
来描述:
之后,我们需要定义一个任务注册类,来管理异步任务的注册的提交工作,这个类我将它命名为:ThreadExcutor
实际上这个类,只是将传入ICallable和IRunable对象,注册到线程池当中来,而运行时机则由线程池去管理和分配。接下来就是实际的任务设计了,在LumQQ.NET中目前有三个这种的任务:
PacketIncomeTrigger:异步处理输入包。只有等到有收到数据后,才提交这个任务,在后台线程中去处理QQ事件。一次处理会全部处理所有已收到的输入包,处理完后退出。当包处理线程正在运行时,试图注册相同任务将自动不会被注册。
KeepAliveTrigger:定时执行发送在线包的任务,它实现的就是Timer组件的功能。
ResendTrigger:这是一个不定时执行的间隔线程。重复发送包,保证每一次重得发送包都是顺序执行的,而不希望同时可能执行多个重复发送
包线程,而且下一次的执行时间也是动态的。要实现这个功能我们只需要在注册等待线程时指定合适的超时时间和executeOnlyOnce为true(只
执行一次),等到任务执行结束后,再重新注册一次定时线程。这样就可以完善解决不定时的间隔线程问题了。
更多详细的功能实现请参考LumaQQ.NET中的相关代码。
》中,讨论了几种.NET Framework定时器异同之处,以及使用场合。我当时的初忠也是在为LumaQQ.NET
寻
找一种最合理的线程定时执行和线程排队的方案。因为在LumaQQ中,有需要定时执行的线程,比如发送KeepLive包;有需要不同间隔时间,间隔时间
是动态的且不应该是“可重入”(前一个执行还没有完成,又再次进行执行定时任务),比如重发包;有需要在一个独立线程上不定期执行的任务,比如收到包后的
事件处理(收到数据包后,程序主线程需要继续去监听数据,而不能在主线程上去进行包的处理工作,这样如果事件代理的执行时间很长的话,主线程的执行周期被
占用了,而造成无法及时接收和发送数据包)。在这三种情况下,直接使用Timer组件肯定无法完全满足我们的要求。
通过分析Timer组件,我们就很容易发现,他们都是通过ThreadPool
这个类的功能来实现定时器的功能。ThreadPool类,提供了对当前进程内的后台线程统一管理,集中调配和资源重用的线程池实现。利用ThreadPool可以实现任务的延迟处理,I/0 异常操作,线程间的互斥执行,定时任务等功能。
ThreadPool提供了一系列管理和维护线程池的接口。在LumaQQ.NET中,我需要的功能就是利用:QueueUserWorkItem 和RegisterWaitForSingleObject 这两个接口来实现的。
QueueUserWorkItem :让我们可以注册一个排队的异步任务,等到线程池有剩余的线程资源时去异步执行任务。
RegisterWaitForSingleObject
:让我们可以注册一个等待信号就绪的异步任务,等到该线程等待的信号就绪后异步执行该任务。这个接口等待信号是一个WaitHandle对象,同时还可以
指定等待超时(间隔时间),而通过设置executeOnlyOnce参数的值可以指定是否重复执行该任务。
了解了ThreadPool的基本情况后,我们就可以来设计我们的异步线程执行框架。首先对异步任务和间隔任务,我们分别定义了两个接口ICallable
和IRunable
来描述:
1: /// <summary>
2: /// 可以被提交到线程池异步处理的接口
3: /// <remark>abu 2008-03-07 </remark>
4: /// </summary>
5: public interface ICallable
6: {
7: /// <summary>
8: /// 是否已经在运行
9: /// <remark>abu 2008-03-07 </remark>
10: /// </summary>
11: /// <value></value>
12: bool IsRunning { get; }
13: /// <summary>
14: /// WaitCallback回调
15: /// <remark>abu 2008-03-07 </remark>
16: /// </summary>
17: /// <param name="state">The state.</param>
18: void Call(object state);
19: }
20: /// <summary>
21: /// 可以被提交到线程池定时运行的接口
22: /// <remark>abu 2008-03-07 </remark>
23: /// </summary>
24: public interface IRunable : IDisposable
25: {
26: /// <summary>是否已经在运行
27: /// <remark>abu 2008-03-07 </remark>
28: /// </summary>
29: /// <value></value>
30: bool IsRunning { get; }
31: /// <summary>
32: /// <remark>abu 2008-03-07 </remark>
33: /// </summary>
34: /// <param name="state">The state.</param>
35: /// <param name="timedOut">if set to <c>true</c> [timed out].</param>
36: void Run(object state, bool timedOut);
37: /// <summary>
38: /// 注册在线程池后的信号变量
39: /// <remark>abu 2008-03-07 </remark>
40: /// </summary>
41: /// <value></value>
42: WaitHandle WaitHandler { get; set; }
43: /// <summary>
44: /// 注册后的对象
45: /// <remark>abu 2008-03-07 </remark>
46: /// </summary>
47: /// <value></value>
48: RegisteredWaitHandle RegisterdHandler { get; set; }
49: }
之后,我们需要定义一个任务注册类,来管理异步任务的注册的提交工作,这个类我将它命名为:ThreadExcutor
1: /// <summary>
2: /// 利用线程池来异步执行线程
3: /// <remark>abu 2008-03-07 </remark>
4: /// </summary>
5: public class ThreadExcutor
6: {
7: /// <summary>提交一个线程等待执行
8: /// <remark>abu 2008-03-07 </remark>
9: /// </summary>
10: /// <param name="callable">The callable.</param>
11: /// <param name="state">The state.</param>
12: public static void Submit(ICallable callable, object state)
13: {
14: if (!callable.IsRunning)
15: {
16: ThreadPool.QueueUserWorkItem(new WaitCallback(callable.Call), state);
17: }
18: }
19: /// <summary>
20: /// 注册一个轮循线程
21: /// <remark>abu 2008-03-07 </remark>
22: /// </summary>
23: /// <param name="runnable">The runnable.</param>
24: /// <param name="state">The state.</param>
25: /// <param name="interval">The interval.</param>
26: public static void RegisterIntervalObject(IRunable runnable, object state, long interval, bool onlyOnce)
27: {
28: // if (runnable.RegisterdHandler == null)
29: // {
30: runnable.WaitHandler = new AutoResetEvent(false );
31: runnable.RegisterdHandler = ThreadPool.RegisterWaitForSingleObject(runnable.WaitHandler, new WaitOrTimerCallback(runnable.Run), state, interval, onlyOnce);
32: // }
33: }
34: }
实际上这个类,只是将传入ICallable和IRunable对象,注册到线程池当中来,而运行时机则由线程池去管理和分配。接下来就是实际的任务设计了,在LumQQ.NET中目前有三个这种的任务:
PacketIncomeTrigger:异步处理输入包。只有等到有收到数据后,才提交这个任务,在后台线程中去处理QQ事件。一次处理会全部处理所有已收到的输入包,处理完后退出。当包处理线程正在运行时,试图注册相同任务将自动不会被注册。
KeepAliveTrigger:定时执行发送在线包的任务,它实现的就是Timer组件的功能。
ResendTrigger:这是一个不定时执行的间隔线程。重复发送包,保证每一次重得发送包都是顺序执行的,而不希望同时可能执行多个重复发送
包线程,而且下一次的执行时间也是动态的。要实现这个功能我们只需要在注册等待线程时指定合适的超时时间和executeOnlyOnce为true(只
执行一次),等到任务执行结束后,再重新注册一次定时线程。这样就可以完善解决不定时的间隔线程问题了。
更多详细的功能实现请参考LumaQQ.NET中的相关代码。
相关文章推荐
- 不定时的间隔线程和线程排队执行(LumaQQ.NET异步线程处理)
- [导入]不定时的间隔线程和线程排队执行(LumaQQ.NET异步线程处理)
- vb.net shell线程执行完成后再运行以后处理 函数
- 使用Eclipse Job执行异步线程的处理
- 异步在工作线程中执行图片模糊化处理 工具
- boost asio的异步事件处理函数是在执行异步事件的run函数所在的线程里面执行的
- C#:异步编程和线程的使用(.NET 4.5 ),异步方法改为同步执行
- Net线程间通信的异步机制
- net中异步执行(一)
- 用Quartz处理定时执行的任务
- 事务处理后执行耗时线程
- 两线程异步执行,中间等待另一线程执行完再执行
- 异步执行线程(转)
- asp.net,mvc做支付宝手机接口notify_url异步通知没有执行
- ASP.Net定时任务执行
- 线程相关——HandlerThread、IntentService、ResultReceiver:结果接收者、AsyncTask:异步任务、Android中处理线程间通信的方式
- 开启三个线程,然后定时执行
- asp.net线程批量导入数据时通过ajax获取执行状态
- Android线程之异步消息处理机制(一)
- apss asp.net C# web 定时执行程序