本文介绍了循环而不会导致应用程序冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个循环来检查变量的值是否已更改.没有任何事件可以告诉我值已更改.该应用程序不支持多线程.如何在不导致应用程序冻结的情况下实现这一目标?

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.

这篇关于循环而不会导致应用程序冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 15:10
查看更多