我想编写一个循环来检查变量的值是否已更改。没有事件可以告诉我值已更改。 该应用程序不支持多线程。 如何在不导致应用程序冻结的情况下实现这一目标?
目标是:
Application starts
...
loop
Check variable value
If changed then
exit
if timedOut then
exit
虽然循环导致应用程序冻结。
谢谢。
*编辑* 这就是我想要的(这段代码是由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 do循环,但是这阻止了WndProc的运行。 谢谢
答案 0 :(得分:-1)
答案取决于您的应用程序需求。有2个简单的解决方案,每个解决方案都有优点和缺点: 1.将定时器置于应用程序并按超时检查值。尊严-这是GUI应用程序最简单的方法(Windows消息循环已存在),另一方面则是缺点-检测值的增量时间已更改。 2.处理Application.OnIdle事件。这种方法的缺点-如果没有人单击GUI元素,则将运行检查过程。
解决方案的专业方法-用复杂的对象包装变量,例如:
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,您就可以处理更改
如果您不想使用多线程,那么一种好的方法是根据协程在多个状态机上分割您的逻辑。
基于AIO框架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.