问题描述
我想编写一个循环来检查变量的值是否已更改.没有任何事件可以告诉我值已更改.该应用程序不支持多线程.如何在不导致应用程序冻结的情况下实现这一目标?
I would like to write a loop that checks the value of a variable has changed. There's no event that fires to tell me the value has changed.The application doesn't support multi threading.How to achieve this without causing app to freeze ?
目标是:
Application starts
...
loop
Check variable value
If changed then
exit
if timedOut then
exit
虽然循环导致应用程序冻结.
While loop causes application to freeze.
谢谢.
*编辑* 这就是我想要的(此代码由Remy Lebeau编写):
* Edit *This is what I'm after (this code is written by Remy Lebeau):
const
APPWM_COM_EVENT_DONE = WM_APP + 1;
APPWM_COM_EVENT_TIMEOUT = WM_APP + 2;
type
MyClass = class
private
MsgWnd: HWND;
procedure COMEventHandler(parameters);
procedure WndProc(var Message: TMessage);
public
constructor Create;
destructor Destroy; override;
procedure DoIt;
end;
constructor MyClass.Create;
begin
inherited;
MsgWnd := AllocateHWnd(WndProc);
end
destructor MyClass.Destroy;
begin
KillTimer(MsgWnd, 1);
DeallocateHWnd(MsgWnd);
inherited;
end;
procedure MyClass.COMEventHandler(parameters);
begin
KillTimer(MsgWnd, 1);
PostMessage(MsgWnd, APPWM_COM_EVENT_DONE, 0, 0);
end;
procedure MyTimer(hWnd: HWND; uMsg: UINT; idEvent: UINT_PTR; dwTime: DWORD); stdcall;
begin
KillTimer(hWnd, idEvent);
PostMessage(hWnd, APPWM_COM_EVENT_TIMEOUT, 0, 0);
end;
procedure MyClass.WndProc(var Message: TMessage);
begin
case Message.Msg of
APPWM_COM_EVENT_DONE:
begin
// Event fired, all good
end;
APPWM_COM_EVENT_TIMEOUT:
begin
// Event timed out
end;
else
begin
Message.Result := DefWindowProc(MsgWnd, Message.Msg, Message.WParam, Message.LParam);
end;
end;
end;
procedure MyClass.DoIt;
begin
SetTimer(MsgWnd, 1, 1000 * 1000, @MyTimer);
// invoke COM function that will eventually trigger the COM event...
end;
如何调用DoIt并等待事件触发或超时而不会导致应用程序冻结?尝试使用while做循环,但是这阻止了WndProc的运行.谢谢
How to call DoIt and wait for either Event to fire or timeout without causing the application to freeze ?Tried using while do loop but that prevented WndProc from running.Thank you
推荐答案
答案取决于您的应用程序需求.有2个简单的解决方案,每个解决方案都有优点和缺点:1.将计时器置于应用程序中,并按超时检查值.尊严-这是GUI应用程序最简单的方法(Windows消息循环已存在),另一方面则是缺点-检测值的增量时间已更改.2.处理Application.OnIdle事件.这种方法的缺点-如果没有人单击GUI元素,则将运行检查过程.
Answer depends on your application demands. There are 2 easy solutions with prons and cons each:1. Put Timer to application and check value by timeout. Dignity - it is the most easy way for GUI application (Windows messages loop already exists), drawback on other side - there will be delta time of detecting value have been changed.2. Handle Application.OnIdle event. Disadvantage of this approach - yor checking procedure will be runned if nobody click on GUI elements.
解决方案的专业方法-用复杂的对象包装变量,例如:
Professional way to solve your solution - wrap your variable by complex object, for example:
Trigger = class
private
FOnChanged: TNotifyEvent;
public
procedure Emit;
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
end;
procedure Trigger.Emit;
if Assined(FOnChanged) then
FOnChanged(Self)
end;
您的应用程序没有线程的原因,我们可以在没有互斥锁/关键部分的情况下实现Trigger,另一方面,一旦事件产生器提高Emit,您就可以处理更改
如果您不想使用多线程,那么一种好的方法是根据协程在多个状态机上分割您的逻辑.
Good approach if you don't want use multithreading is split your ligic on multiple state machines based on coroutines.
基于AIO框架的示例 https://github.com/Purik/AIO
Example based on AIO framework https://github.com/Purik/AIO
AIO框架创建自己的事件循环,在没有线程的情况下并行调度多个状态机:
program TriggerExample;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
SyncObjs,
Gevent,
Greenlets;
const
WAIT_TMEOUT_MSEC = 1000;
var
ChangedEvent: TGevent;
Value: Boolean = False;
// Part of application that raise change events randomly
procedure EventsProducer;
begin
while True do
begin
Greenlets.GreenSleep(100+Random(10000));
Value := True;
ChangedEvent.SetEvent;
end;
end;
begin
ChangedEvent := TGevent.Create(False, False);
// run fake event producer inside other state machine
TSymmetric.Spawn(EventsProducer);
// Loop
while True do
begin
if ChangedEvent.WaitFor(WAIT_TMEOUT_MSEC) = wrSignaled then
begin
WriteLn('Value was changed');
Value := False
end
else
begin
WriteLn('Exit by timeout');
end;
end;
end.
这篇关于循环而不会导致应用程序冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!