小议Timer重入问题 在Lazarus中 ,TTimer是一个定时器,他封装了API,将按一定时间自动执行 ontimer这个句柄指向的回调函数。 那么这个回调是否就是一个线程呢? 很明显不是一个单独的线程,下面这个例子就可以说明,主要代码: Var t:integer; procedure TForm1.Timer1Timer(Sender: TObject); begin inc(t); memo1.lines.add((sender as ttimer).name + ':' + inttostr(t)); end; procedure TForm1.FormCreate(Sender: TObject); begin t :=0; memo1.lines.add('formcreate:' + inttostr(t)); end; procedure TForm1.Button1Click(Sender: TObject); begin timer1.Enabled := false; timer2.enabled := false; end; procedure TForm1.Timer2Timer(Sender: TObject); begin inc(t); memo1.lines.add((sender as ttimer).name + ':' + inttostr(t)); end; 就是两个定时器,定时修改全局变量t,并且显示是哪个修改的,当前值是多少。并且 我们在任务管理器里面查看,发现线程数一直都是1. 那么这个回调函数相当于什么呢? 还是主线程中的一块普通代码,和button1click 没有什么大的区别。否则如果是多个线程,就需要互斥访问变量t了,我们并没有加入这样的代码。 那么小的区别在哪里? Ttimer的定时回调函数,是由系统时钟发起的消息,执行的。也就是说,如果我们在里面执行显示模态窗口等常规情况下会暂停主线程的方法会失效,这是其一。 第2点就是,无论这个回调函数有没有执行完毕,只要下一个时间到,回调函数将会重头开始执行。下面这个例子就能很好的表达这个问题。 procedure TForm1.Timer1Timer(Sender: TObject); begin inc(t); application.MessageBox('','', 0); inc(t); memo1.lines.add((sender as ttimer).name + ':' + inttostr(t)); end; procedure TForm1.FormCreate(Sender: TObject); begin t :=0; memo1.lines.add('formcreate:' + inttostr(t)); end; procedure TForm1.Button1Click(Sender: TObject); begin timer1.Enabled := false; timer2.enabled := false; end; procedure TForm1.Timer2Timer(Sender: TObject); begin inc(t); memo1.lines.add((sender as ttimer).name + ':' + inttostr(t)); end; 注意 application.MessageBox('','', 0); 这句, 明明我们已经将主线程用显示窗口暂停了的样子,但是调试器却显示timer时间到,代码又从头开始执行了,导致application对象试图反复创建模态窗口,这是不正确的,也只有timer能够做到,没有关闭模态窗口,又创建并显示模态窗口,但是会报错。 file:///C:/WINDOWS/TEMP/ksohtml/wps_clip_image1.png 那么如何解决此类问题。 在此回调函数的开始第1句,使用 (sender as ttimer).enabled := false; 再在需要的地方进行 enabled :=true; 如果是多个timer ,也可以将所有timer 禁止,在需要的地方恢复。 根据业务逻辑。 By steven Fpccn.com 2013年4月
|