2016-11-12 427 views
-2

有此几乎遗留代码,涉及一个简单的TThread,用作定时器,基于WaitForSingleObject的()和事件句柄,像这样WaitForSingleObject的带超时的超时

TTimerThread = class(TThread) 
private 
    FInterval: cardinal; 
    FEvent: THandle; 
    FSomeClass: TSomeClass; 
protected 
    procedure Execute; override; 
end; 

.... 

procedure TTimerThread.Execute; 
var res: cardinal; 
begin 
    repeat 
    log('Start WaitForSingleObject() with %d', [FInterval]); 
    res := WaitForSIngleObject(FEvent, FInterval); 
    log('End WaitForSingleObject() with result %d', [res]); 
    if res = WAIT_TIMEOUT then 
     if not Terminated then 
     Synchronize(FSomeClass.SomeMethod); 
    until Terminated; 
end; 

的代码之后返回长有些应用程序特定的失败检查(何时不会触发)和日志记录方面有点缩减。

的日志调用会在类似于日志文件中显示:

2016/11/12 17:49:08:056 $1130 llDebug Start WaitForSingleObject() with 20 
2016/11/12 17:49:09:015 $1130 llDebug End WaitForSingleObject() with result 258 

登录功能与格式打印的值现在,$ 1130当前线程,llDebug是记录等级。这两个调用之间没有任何记录(日志文件是“功能”/“模块”)

在这种情况下,等待时间高达959毫秒!

的下降事件件在主线程(就像线程计时器本身),像这样创建:

FEvent := CreateEvent(nil, false, false, nil); 

所以线程本身不会产生窗户,也没有使用COM或类似的东西。如果SomeMethod会使用这种方法,它将在一个Synchronized调用中,以便在主线程中执行。然而,对于这个特定的测试,SomeMethod只是在TImage上绘制。

该代码计算FInterval为20 ms。该线程大约每30/31 ms触发一次,最有可能是由于Windows计时器分辨率。

我们有1个客户运行Windows 10,其中WaitForSingleObject(),每隔几分钟(相隔好几分钟)只会在400+毫秒后返回。

SomeMethod在1 ms内执行,因为它没有做太多处理。

我们不需要高分辨率的计时器,因为当前的代码在其他地方工作的很好,每30毫秒一次就足够了,即使是10-15毫秒的“错误”也是如此。

定时器控制一堆操作,这就是为什么它以大约20ms的间隔执行,但是对于这个问题,我们已经消除了所有其他操作(显式地),只剩下1个操作正在运行,这就是我们如何调试这个请参阅WaitForSingleObject()每隔几分钟400或400 ms后不返回。

在WaitForSingleObject()之前有一个日志调用,并且在一个之后(也记录了间隔),所以100%确定WaitForSingleObject()在400ms以上返回,即使时间间隔是20ms。 如预期的那样,日志记录显示WaitForSingleObject()的返回值为WAIT_TIMEOUT。

问题是:在WaitForSingleObject()中可能导致此行为的原因是什么? 我的意思是我可以理解几个ms额外由于CPU繁忙,太多的线程(不是在这个应用程序的情况下),但在峰值负载小于30%的系统上差不多半秒是奇怪的。

感谢

回答

-1

考虑到问题的日志调用,在定时器线程的Execute程序,如果他们最终写入日志文件在同一个线程,这将阻止,直到写操作完成。由于在第一次写入操作之前检索到开始时间,因此日志间隔不仅包含等待时间(WaitForSingleObject),还包含第一次写入操作。这可以解释一般偏向30ms而不是20ms。行为异常的I/O子系统或拥塞也可以解释偶尔的延长时间段。

检索等待操作前的开始时间,但不写入。等待呼叫返回后写入开始和结束时间。然后日志会更准确地反映等待时间,最有可能的是延长的延迟将会变成I/O限制。 WaitForSingleObject不太可能不准确。

也考虑卸载日志写入到工作线程。