线程回调函数里创建定时器后,无法启用 ( 积分: 100 )

  • 主题发起人 主题发起人 zhoulingj
  • 开始时间 开始时间
Z

zhoulingj

Unregistered / Unconfirmed
GUEST, unregistred user!
在TForm1里有一个变量声明,_timer: TTimer;
然后有个线程TThreadA,线程里有个回调函数,函数定义是在TForm1里,在回调函数里创建定时器_timer := TTimer.Create(nil);可是启用定时器后,发现没有反应,不知道是怎么回事?
另外:定时器不是基于消息的吗?为什么定时器变量在线程类里声明并创建后,启用后可以正常使用呢?线程不是没有消息队列吗?请各位指教...
 
LZ大概没有看过SetTimer和KillTimer这两个API的说明,定时有两种方式:一种是通过窗口消息,一种是通过回调函数。调用SetTimer时可以指定使用何种方式。至于你的问题,你可以看看TTimer组件的源码,就知道答案了。
 
帮你顶一下,接个分:
欢迎加入delphi交流群:4654765(delphi天堂),让我们共同进步!!
 
时器是个很有意思的东西,它很有用,但我认为这不是现代计算机的结构所擅长的事情。
计算机适合做那些很大量的简单重复工作,或者根据请求做出回应。
DOS时代是没有进程线程等概念的,那时候要想做到定时真是有些麻烦
通常的做法是死循环不断监测时间,发现时间到了就做特定的事情
当然你可以用delay,来指定等待多长时间,但是如果你一边要响应用户的操作,比如输入,一边要定时做些
事情就是一件麻烦的事了
当然有些人可以这样做,截取系统的时钟中断(我忘了中断号是多少了),每秒钟有18.2次
当这些做法都不是很优雅。但DOS时代只能这样凑合着了
Windows是个伟大的进步,系统提供了Timer支持,但是问题是这个定时器并不准时而且有时候根本不能用。
Win32 API中有个SetTimer函数,可以为一个窗口创建一个定时器,这个定时器会定时产生消息WM_TIMER也可以调用
指定的回调函数,其实这都是一样的,因为都是单线程的。
单线程的定时器会有很多问题,首先是不准时,定时器只是定时把消息WM_TIMER访到线程的消息队列里,但是并不保证消息会立刻被响应,如果
碰巧系统比较忙,那么消息可能会在队列里放一端时间才被响应,还会造成本来应该间隔一段时间发生的消息响应连续发生了
解决方法通常是
OnTimer(...)
{
//Timer process.....

MSG msg;
While(PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE));
}
在当前Timer处理中,把消息队列里的WM_TIMER消息,清除掉。
更糟的是如果你不去调用GetMessage,那么就不会有Timer发生了。
这个问题直到win xp都没什么改变,似乎微软并不打算在Win32 API中解决这个问题了。
.NET Framework为我们带来了新的解决方案
.NET Framework提供三种Timer
Server Timers System.Timers.Timer
Thread Timers System.Threading.Timer
Windows Timers System.Windows.Forms.Timer
其中Windows Timers只是提供了和WinAPI 一样的Timer,仍然是基于消息,仍然是单线程
其它两个就不同了,他们是基于线程池的Thread Pool,这样最大的好处在于,产生的时间间隔准确均匀。
Server Timers 和 Thread Timers 的不同在于ServerTimers 是基于事件的,Thread Timers是基于回调函数
我更喜欢Thread Timer,比较轻量级方便易用。
但是这样的Timer也有问题,就是由于时多线程定时器,就会出现如果一个Timer处理没有完成,到了时间下一个
照样会发生,这就会导致重入问题
对付重入问题通常的办法是加锁,但是对于 Timer却不能简单的这样做,你需要评估一下
首先Timer处理里本来就不应该做太需要时间的事情,或者花费时间无法估计的事情,比同远方的服务器建立一个网络连接,这样的做法尽量避免
如果实在无法避免,那么要评估Timer处理超时是否经常发生,如果是很少出现,那么可以用lock(Object)的方法来防止重入
如果这种情况经常出现呢?那就要用另外的方法来防止重入了
我们可以设置一个标志,表示一个Timer处理正在执行,下一个Timer发生的时候发现上一个没有执行完就放弃执行
static int inTimer = 0;
public static void threadTimerCallback(Object obj)
{
if ( inTiemr == 0 )
{
inTimer = 1;

Console.WriteLine("Time:{0}, /tThread ID:{1}", DateTime.Now, Thread.CurrentThread.GetHashCode());
Thread.Sleep(2000);
inTimer = 0;
}
}
但是在多线程下给inTimer赋值不够安全,还好Interlocked.Exchange提供了一种轻量级的线程安全的给对象赋值的方法
static int inTimer = 0;
public static void threadTimerCallback(Object obj)
{
if ( Interlocked.Exchange(ref inTimer, 1) == 0 )
{
Console.WriteLine("Time:{0}, /tThread ID:{1}", DateTime.Now, Thread.CurrentThread.GetHashCode());
Thread.Sleep(250);
Interlocked.Exchange(ref inTimer, 0);
}
}
 
to 地质灾害:
我看了TTimer的源码,那个定时器是基于窗口消息的,但我怎么都不明白,为什么定时器变量声明在Form里,而在线程的回调里创建时不能启用,而声明在线程类里,并在线程类里创建是可用的,呵,当然我并不是软件里一定要用这种创建方法,只是我在写一个小程序的时候发现了这个问题,想搞明白,呵,请明白的大哥们详细的解释一下,可以吗?谢谢!
 
我又试了下,定时器变量不管声明在哪里,只要是在线程中创建,都不能启动.
我以前没有想明白,以为在线程的Create里面创建定时器也叫在线程中创建的,事实上那还是在主线程里创建的...不过现在似乎明白了,在线程里创建的定时器没有消息队列,所以不能用....
 
后退
顶部