解决kbmMW Scheduler在任务中停止任务遇到的问题
kbmMW提供了强大的Scheduler Framework,用来做调度任务的算法,在我看来,Scheduler发展了几个版本之后,截至到目前为止,更象是一个多线程框架,用来开发多线程算法。关于如何应用Scheduler,我翻译了作者写过的所有文章,可以去查看。今天要写的是实际项目中遇到的一个问题,如何用Scheduler来解决以及在解决过程中遇到的问题。
先看下需求:
有一个定位任务,在定位时,我显示一个等待界面,如果定位超时,则隐藏这个等待界面。下面是写的第一个版本的代码,建立一个每秒执行一次的调度任务,如果超时,则隐藏等待定位结果的界面,并停止调度任务。这里让调度任务与主线程同步执行:
procedure TCustomGridViewCameraDoc.InitSchedule; begin Scheduler.Schedule( procedure(const AScheduledEvent:IkbmMWScheduledEvent) begin //隐藏等待定位界面
... AScheduledEvent.Terminate; //停止任务(必须 .SyncQueued) end).EverySecond(1).Synchronized.NamedAs('se_job1') end;
上面的代码出问题了,当调用AScheduledEvent.Terminate,应用卡死!
换一种写法,测试正常:
procedure TCustomGridViewCameraDoc.InitSchedule; begin se_job1 := Scheduler.Schedule( procedure(const AScheduledEvent:IkbmMWScheduledEvent) begin ... AScheduledEvent.Terminate;//正常! end).EverySecond(1) .SyncQueued//Synchronized换成SyncQueued .NamedAs('se_job1') end;
或者这样写,也正常:
procedure TCustomGridViewCameraDoc.InitSchedule; begin se_job1 := Scheduler.Schedule( procedure(const AScheduledEvent:IkbmMWScheduledEvent) begin TThread.Synchronize(nil,procedure begin ....; end);//这样同步主线程执行逻辑 AScheduledEvent.Terminate; end).EverySecond(1).NamedAs('se_job1') end;
算不算是bug呢?我也说不清了。
进一步测试,又发现问题,当用下面代码重新激活这个调度事件(ScheduledEvent)发现无法激活,即调度任务不执行了。
Scheduler.Events.GetByName('se_job1').Activate;
通过把AScheduledEvent.Terminate替换成AScheduledEvent.Activate(False),问题得到解决。通过这个问题可以得出结论:当调用Terminate方法后,这个事件将无法重新Activate。
下面是最后的版本:
procedure TCustomGridViewCameraDoc.InitSchedule; begin //避免重复建立调度事件 if Scheduler.Events.GetByName('se_job1') <> nil then exit; //建立一个调度事件,命名为se_job1 Scheduler.Schedule( procedure(const AScheduledEvent:IkbmMWScheduledEvent) begin AScheduledEvent.Activate(False);//必须 .SyncQueued end) .EverySecond(1) .SyncQueued .NamedAs('se_job1') end;