Delphi : 不冻结和处理消息的睡眠
Delphi : Sleep without freeze and processmessages
我需要一种暂停函数执行几秒钟的方法。我知道我可以使用 sleep 方法来做到这一点,但是这个方法 'freezes' 应用程序在执行时。我也知道我可以使用类似下面的代码来避免冻结:
// sleeps for 5 seconds without freezing
for i := 1 to 5 do
begin
sleep(1000);
application.processmessages;
end;
这种方法有两个问题:一个是冻结仍然每秒发生一次,第二个问题是每秒调用 'application.processmessages'。我的应用程序是 CPU 密集型应用程序,每个 processmessages 调用都会做很多不必要的工作,使用不必要的 CPU 电源;我只想暂停工作流程,仅此而已。
我真正需要的是一种像 TTimer 一样暂停执行的方法,在下面的示例中:
// sleeps for 5 seconds
mytimer.interval := 5000;
mytimer.enabled := true;
// wait the timer executes
// then continue the flow
// running myfunction
myfunction;
这种方法的问题是 'myfunction' 不会等待 mytimer,它将在启用 mytimer 后立即 运行。
是否有另一种方法可以实现我想要的暂停?
提前致谢。
将需要暂停的任务移到单独的线程中,以免干扰 UI。
令人怀疑的是,Application.ProcessMessages真的会消耗太多处理器时间。您可以尝试存储开始等待的时间,然后开始重复 Application.ProcessMessages 直到...;循环检查存储时间和当前时间之间的时间跨度。
正如 David 所说,最好的 选项是将工作移至单独的线程中并完全停止阻塞主线程。但是,如果你必须阻塞主线程,那么至少你应该只在确实有消息等待处理时才调用 ProcessMessages()
,让线程在其余时间休眠。您可以使用 MsgWaitForMultipleObjects()
来处理,例如:
var
Start, Elapsed: DWORD;
// sleep for 5 seconds without freezing
Start := GetTickCount;
Elapsed := 0;
repeat
// (WAIT_OBJECT_0+nCount) is returned when a message is in the queue.
// WAIT_TIMEOUT is returned when the timeout elapses.
if MsgWaitForMultipleObjects(0, Pointer(nil)^, FALSE, 5000-Elapsed, QS_ALLINPUT) <> WAIT_OBJECT_0 then Break;
Application.ProcessMessages;
Elapsed := GetTickCount - Start;
until Elapsed >= 5000;
或者:
var
Ret: DWORD;
WaitTime: TLargeInteger;
Timer: THandle;
// sleep for 5 seconds without freezing
Timer := CreateWaitableTimer(nil, TRUE, nil);
WaitTime := -50000000; // 5 seconds
SetWaitableTimer(Timer, WaitTime, 0, nil, nil, FALSE);
repeat
// (WAIT_OBJECT_0+0) is returned when the timer is signaled.
// (WAIT_OBJECT_0+1) is returned when a message is in the queue.
Ret := MsgWaitForMultipleObjects(1, Timer, FALSE, INFINITE, QS_ALLINPUT);
if Ret <> (WAIT_OBJECT_0+1) then Break;
Application.ProcessMessages;
until False;
if Ret <> WAIT_OBJECT_0 then
CancelWaitableTimer(Timer);
CloseHandle(Timer);
如果您不介意使用定时器,您可以这样做:
(ouside the timer-event:)
mytimer.interval := 5000;
mytimer.tag:=0;
mytimer.enabled := true;
(inside the timer-event:)
mytimer.tag:=mytimer.tag+1;
if mytimer.tag=2 then begin
mytimer.enabled:=false;
myfunction;
end;
我需要一种暂停函数执行几秒钟的方法。我知道我可以使用 sleep 方法来做到这一点,但是这个方法 'freezes' 应用程序在执行时。我也知道我可以使用类似下面的代码来避免冻结:
// sleeps for 5 seconds without freezing
for i := 1 to 5 do
begin
sleep(1000);
application.processmessages;
end;
这种方法有两个问题:一个是冻结仍然每秒发生一次,第二个问题是每秒调用 'application.processmessages'。我的应用程序是 CPU 密集型应用程序,每个 processmessages 调用都会做很多不必要的工作,使用不必要的 CPU 电源;我只想暂停工作流程,仅此而已。
我真正需要的是一种像 TTimer 一样暂停执行的方法,在下面的示例中:
// sleeps for 5 seconds
mytimer.interval := 5000;
mytimer.enabled := true;
// wait the timer executes
// then continue the flow
// running myfunction
myfunction;
这种方法的问题是 'myfunction' 不会等待 mytimer,它将在启用 mytimer 后立即 运行。
是否有另一种方法可以实现我想要的暂停?
提前致谢。
将需要暂停的任务移到单独的线程中,以免干扰 UI。
令人怀疑的是,Application.ProcessMessages真的会消耗太多处理器时间。您可以尝试存储开始等待的时间,然后开始重复 Application.ProcessMessages 直到...;循环检查存储时间和当前时间之间的时间跨度。
正如 David 所说,最好的 选项是将工作移至单独的线程中并完全停止阻塞主线程。但是,如果你必须阻塞主线程,那么至少你应该只在确实有消息等待处理时才调用 ProcessMessages()
,让线程在其余时间休眠。您可以使用 MsgWaitForMultipleObjects()
来处理,例如:
var
Start, Elapsed: DWORD;
// sleep for 5 seconds without freezing
Start := GetTickCount;
Elapsed := 0;
repeat
// (WAIT_OBJECT_0+nCount) is returned when a message is in the queue.
// WAIT_TIMEOUT is returned when the timeout elapses.
if MsgWaitForMultipleObjects(0, Pointer(nil)^, FALSE, 5000-Elapsed, QS_ALLINPUT) <> WAIT_OBJECT_0 then Break;
Application.ProcessMessages;
Elapsed := GetTickCount - Start;
until Elapsed >= 5000;
或者:
var
Ret: DWORD;
WaitTime: TLargeInteger;
Timer: THandle;
// sleep for 5 seconds without freezing
Timer := CreateWaitableTimer(nil, TRUE, nil);
WaitTime := -50000000; // 5 seconds
SetWaitableTimer(Timer, WaitTime, 0, nil, nil, FALSE);
repeat
// (WAIT_OBJECT_0+0) is returned when the timer is signaled.
// (WAIT_OBJECT_0+1) is returned when a message is in the queue.
Ret := MsgWaitForMultipleObjects(1, Timer, FALSE, INFINITE, QS_ALLINPUT);
if Ret <> (WAIT_OBJECT_0+1) then Break;
Application.ProcessMessages;
until False;
if Ret <> WAIT_OBJECT_0 then
CancelWaitableTimer(Timer);
CloseHandle(Timer);
如果您不介意使用定时器,您可以这样做:
(ouside the timer-event:)
mytimer.interval := 5000;
mytimer.tag:=0;
mytimer.enabled := true;
(inside the timer-event:)
mytimer.tag:=mytimer.tag+1;
if mytimer.tag=2 then begin
mytimer.enabled:=false;
myfunction;
end;