循环而不会导致应用程序冻结 [英] Looping without causing app to freeze

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

问题描述

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

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.

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆