当我使用绝对时间和waitformultipleobjects()时,我有一个等待计时器对象的小问题。
一旦收到计时器信号,我就需要重置对象,而我唯一能找到的方法是再次使用setwaitabletimer,并且将来的到期时间不切实际。这将毫无理由地使计时器在OS唤醒计时器中保持活动状态。 我需要保留该句柄,因为在某些情况下需要重新激活计时器,并且我在waitformultipleobjects数组中使用了计时器对象,因此关闭FWaitTimer句柄将不是很好。
我是否正确理解此方法?或者有更好的方法吗?
我的完整代码如下所示,请忽略其他waitfor对象,它只是FWaitTimer对象。
unit frmMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.WinXPickers, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, dxSkinsCore,
dxSkinTheAsphaltWorld, dxSkinsDefaultPainters, cxCalendar, Vcl.ExtCtrls, cxContainer, cxEdit, dxCore, cxDateUtils, cxDropDownEdit, cxTextEdit, cxMaskEdit,
cxSpinEdit, cxTimeEdit, Vcl.Menus, Vcl.StdCtrls, cxButtons, dateutils, syncObjs, UTimer;
const
WM_WATINGDONE = WM_USER + 100;
type
TMyWaitThread = class(TThread)
private
FWaitTimer : THandle;
FTerminateEvent : TEvent;
FPeriodicTimer : TADTimer;
FWaitTime: TDateTime;
procedure SetWaitTime(const Value: TDateTime);
property WaitTime : TDateTime read FWaitTime write SetWaitTime;
public
constructor Create; overload;
constructor Create(CreateSuspended: Boolean); overload;
destructor Destroy; override;
procedure execute; override;
procedure Terminate;
end;
TForm1 = class(TForm)
cxClock1: TcxClock;
Timer1: TTimer;
cxDateEdit1: TcxDateEdit;
btnWait: TcxButton;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnWaitClick(Sender: TObject);
private
FMyWaitThread : TMyWaitThread;
Fwaiting: Boolean;
procedure WM_WAITINGDONE(var msg : TMessage); message WM_WATINGDONE;
procedure Setwaiting(const Value: Boolean);
{ Private declarations }
property waiting : Boolean read Fwaiting write Setwaiting;
procedure WatingDone;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnWaitClick(Sender: TObject);
var
dt : TDateTime;
begin
dt := cxDateEdit1.EditValue;
FMyWaitThread.SetWaitTime(dt);
waiting := True;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
cxClock1.Time := now;
Timer1.Enabled := True;
waiting := False;
FMyWaitThread := TMyWaitThread.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FMyWaitThread.Terminate;
FreeAndNil(FMyWaitThread);
end;
procedure TForm1.Setwaiting(const Value: Boolean);
begin
Fwaiting := Value;
case Fwaiting of
True : begin
btnWait.Enabled := False;
end;
false : begin
btnWait.Enabled := True;
cxDateEdit1.EditValue := IncMinute(Now, 1);
end;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
cxClock1.Time := now;
end;
procedure TForm1.WatingDone;
begin
waiting := False;
end;
procedure TForm1.WM_WAITINGDONE(var msg: TMessage);
begin
waiting := False;
end;
{ TMyWaitThread }
function GetGUID : string;
var
uid : TGUID;
r : Integer;
begin
Result := '';
r := CreateGuid(Uid);
if r = S_OK then
begin
Result := StringReplace(GuidToString(Uid),'{', '', [rfReplaceAll]);
Result := StringReplace(result, '}', '', [rfReplaceAll]);
end;
end;
constructor TMyWaitThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FWaitTimer := CreateWaitableTimer(nil, True, PWideChar(GetGUID)); // change the 'MatrixTimer' to something unique...
FTerminateEvent := TEvent.Create;
FPeriodicTimer := TADTimer.Create;
FPeriodicTimer.Interval := 10000;
end;
destructor TMyWaitThread.Destroy;
begin
FPeriodicTimer.StopTimer;
FreeAndNil(FPeriodicTimer);
FreeAndNil(FTerminateEvent);
CloseHandle(FWaitTimer);
inherited;
end;
procedure TMyWaitThread.execute;
var
EventArr : array of THandle;
begin
SetLength(EventArr, 3);
EventArr[0] := FTerminateEvent.Handle;
EventArr[1] := FWaitTimer;
EventArr[2] := FPeriodicTimer.Handle;
while not Terminated do
begin
try
case WaitForMultipleObjects(Length(EventArr), @EventArr[0], False, INFINITE) of
WAIT_OBJECT_0 : begin // terminate
OutputDebugString('Terminating.....');
Exit;
end;
WAIT_OBJECT_0+1 : begin // wait timer
OutputDebugString('Wait timer was triggered');
WaitTime := IncMinute(now, 1);
// How do I reset the FWaitTimer - I would like to keep the FWaitTimer handle so closing it is no good.
PostMessage(Form1.Handle, WM_WATINGDONE, 0, 0);
end;
WAIT_OBJECT_0+2 : begin // periodic timer
OutputDebugString('Periodic timer was triggered');
end;
end;
except on E: Exception do
// keep any exceptions inside the loop
end;
end;
end;
procedure TMyWaitThread.SetWaitTime(const Value: TDateTime);
var
WakeUpTime: LARGE_INTEGER;
SysTime : _SystemTime;
FTime : _FileTime;
begin
FWaitTime := Value;
DateTimeToSystemTime(FWaitTime, SysTime);
SystemTimeToFileTime(SysTime, FTime);
LocalFileTimeToFileTime(FTime, FTime);
WakeUpTime.LowPart := FTime.dwLowDateTime;
WakeUpTime.HighPart := FTime.dwHighDateTime;
SetWaitableTimer(FWaitTimer, WakeUpTime.quadpart, 0, nil, nil, True);
end;
procedure TMyWaitThread.Terminate;
begin
FTerminateEvent.SetEvent;
inherited;
end;
constructor TMyWaitThread.Create;
begin
Create(False);
end;
end.
答案 0 :(得分:3)
documentation(重点是我的话)中的语录:
该线程调用SetWaitableTimer函数来激活计时器。 请注意SetWaitableTimer使用以下参数: lpDueTime参数指定计时器的时间 设置为信号状态。 将手动重置计时器设置为 信号状态,它将一直保持此状态,直到SetWaitableTimer 建立新的到期时间。。当同步计时器设置为 信号状态,直到线程完成 等待计时器对象上的操作。
所以唯一的方法是再次调用SetWaitableTimer
以重置计时器的信号状态。
有2种方法可以解决您的问题,请使用当前的技巧,但在CancelWaitableTimer()
之后添加SetWaitableTimer()
以取消计时器(其状态将保持无信号)
更好的解决方案是不使用计时器时不等待计时器, 按照@RbMm的建议实施:
将FWaitTimer放置在数组的最后2个位置-EventArr [2]:= FWaitTimer并将3或2作为第一个参数传递给WaitForMultipleObjects