Application.ProcessMessages виснет? - PullRequest
3 голосов
/ 12 июня 2010

В моем однопоточном приложении delphi 2009 (еще не завершенном) возникла проблема с зависанием Application.ProcessMessages. В моем приложении есть объект TTimer, который запускается каждые 100 мс для опроса внешнего устройства. я использую Application.ProcessMessages для обновления экрана, когда что-то меняется, поэтому приложение все еще реагирует.

один из них был в событии OnMouseDown сетки. там он имел Application.ProcessMessages, которые по существу зависали. удалить это не было проблемой, за исключением того, что я вскоре обнаружил еще одно Application.ProcessMessages, которое также блокировало.

Я думаю, что со мной может случиться то, что TTimer - в режиме приложения, который я сейчас отлаживаю - вероятно, занимает слишком много времени для завершения. я не позволил обработчику событий TTimer.OnTimer повторно ввести тот же код (см. ниже):

procedure TfrmMeas.tmrCheckTimer(Sender: TObject);
begin
  if m_CheckTimerBusy then
    exit;

  m_CheckTimerBusy:=true;
  try
    PollForAndShowMeasurements;
  finally
    m_CheckTimerBusy:=false;
  end;
end;

в каких местах вызывать Application.ProcessMessages было бы плохой практикой? Подпрограммы OnPaint приходят на ум как нечто, что не имеет смысла.

какие-нибудь общие рекомендации?

Я удивлен, увидев, что такого рода проблемы возникают на данном этапе разработки!

Ответы [ 5 ]

4 голосов
/ 14 июня 2010

Моя рекомендация относительно TApplication.ProcessMessages состоит в том, чтобы никогда не использовать его - просто нет смысла размещать его.

Представьте себе, что делает вызов: ваше приложение запускает цикл сообщений - где сообщения Windows (создаваемые ОС, другими приложениями, вашим приложением и т. Д.) Последовательно обрабатываются - и там, в середине одного из сообщений-процессы, вы просто заново запускаете весь цикл сообщений, не имея контроля над тем, какие сообщения будут обрабатываться, сколько их будет, если какое-либо из сообщений войдет в свой собственный цикл сообщений ... и если ониесть проблемы с повторным входом или нет.Это то, что я называю приглашением неприятностей .

Иногда существуют веские причины для обработки некоторых сообщений Windows (особенно для того, чтобы не вешать другие потоки) или для обработки всех сообщений, направленных в конкретное окно, но это может быть выполнено более тонкопути, с большим контролем.

Если вам необходимо выполнить какую-либо обработку в главном потоке графического интерфейса и вы просто хотите обновить интерфейс, вы можете использовать метод TWinControl.Repaint для перерисовки элементов графического интерфейса.Если вы хотите, чтобы приложение реагировало на пользовательский ввод, вам, в основном, нужно использовать потоки backgroud / worker.

Примечание: в Delphi при любой длительной обработке в основном потоке, особенно, если требуется ожидание, выпредполагается периодически вызывать CheckSynchronize, чтобы позволить другим потокам синхронизироваться с основным потоком - они могут (и, вероятно, будут) зависать в противном случае.VCL вызывает это только тогда, когда приложение бездействует и обрабатывает сообщение WM_NULL (которое ничего не делает, что также может вызвать некоторые интересные побочные эффекты).

2 голосов
/ 14 июня 2010

спасибо всем за ваши комментарии / предложения.

здесь я сделал тестовое приложение, которое имеет подпрограмму таймера, которая занимает больше времени, чем установленный для него интервал. когда я нажимаю button1, Application.ProcessMessages зависает. мое решение на данный момент состоит в том, чтобы отключить таймер во время процедуры таймера.

позже мы планируем поместить «связь с устройством» в поток.

спасибо! Т. пл

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    Memo1: TMemo;
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  memo1.Lines.Add('text 1');

  // this call blocks
  Application.ProcessMessages;

  memo1.Lines.Add('text 2');
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  iTime:cardinal;
begin
  // fix by adding this:  timer1.Enabled:=false;

  iTime:=GetTickCount;
  while GetTickCount-iTime<200 do
    ;

  // fix by adding this:  timer1.Enabled:=true;
end;

end.

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 286
  ClientWidth = 426
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 8
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Memo1: TMemo
    Left = 112
    Top = 8
    Width = 297
    Height = 233
    Lines.Strings = (
      'Memo1')
    TabOrder = 1
  end
  object Timer1: TTimer
    Interval = 100
    OnTimer = Timer1Timer
    Left = 200
    Top = 144
  end
1 голос
/ 13 июня 2010

Используйте madExcept, и вы увидите, где находится тупик.

1 голос
/ 12 июня 2010

Я рекомендую эту статью: " Как отладить зависание приложения? ";)

0 голосов
/ 14 июня 2010

Кажется ужасным стиль сильно полагаться на логику на основе таймера, и вы понимаете, что, как вы говорите, вы планируете использовать потоки в будущем.

Вот прогрессия в вашем коде:

  1. У вас есть сообщение окна таймера. это делает небольшую работу.
  2. Шесть месяцев спустя ваш таймер выполняет много кода.
  3. вы думаете, что могли бы улучшить ситуацию, добавив Application.ProcessMessages.

Давайте немного увеличим таймер с интервалом 100 мс для худшего случая:

09:00:00.000 - timer event fires
09:00:00.100 - we are half way through our Timer Event code. We hit an Application.ProcessMessages.
09:00:00.101 - timer event fires again, but the first time into it, has not yet completed.
09:00:00.200 - we are half way through our timer event code, the second time,
and hit APplication.ProcessMessages again.

Можете ли вы увидеть ... Можете ли вы увидеть .... Можете ли вы увидеть ..... проблему здесь?

W

...