Как я могу минимизировать накладные расходы при обработке сообщений в длинном цикле - PullRequest
4 голосов
/ 04 августа 2010

В моей программе на Delphi есть несколько длинных, но простых циклов, которые могут повторяться миллионы раз и занять несколько секунд.Код внутри цикла очень быстрый и оптимизирован.Это занимает много времени, потому что это делается очень много раз.

Например:

Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
  { do something simple with R.Value }
  R := R.NextRecord;
end;
Screen.Cursor := crDefault;

Теперь я не хочу, чтобы моя программа не отвечала, поэтому я хочу добавить приложение.ProcessMessages внутри цикла.Но я также хочу, чтобы добавленные операторы как можно меньше замедляли мой цикл.

Я слежу за связанным списком, поэтому у меня даже нет подсчитываемой переменной, и мне нужно было бы добавить ее, если бы я хотелинтервалы.Или я должен был бы добавить таймер, но нужно минимизировать проверку времени.

Как мне реализовать это, чтобы минимизировать добавленные накладные расходы?


Вывод:

Пока что я делаю что-то вроде ответа APZ28.

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

Ответы [ 5 ]

8 голосов
/ 04 августа 2010

Можете ли вы поместить рабочий цикл в поток, освобождая основной поток для обработки цикла GUI.

4 голосов
/ 04 августа 2010

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

var
  LoopCounter: Integer;

LoopCounter := 0;
R := FirstRecord;
while R <> nil do begin
  Inc(LoopCounter);
  if (LoopCounter >= ???) then
  begin
    LoopCounter := 0;
    Application.ProcessMessages;
  end;

  { do something simple with R.Value }
  R := R.NextRecord;
end;
2 голосов
/ 04 августа 2010

Лучший вариант - переместить цикл в собственный рабочий поток, чтобы основной поток не блокировался, тогда вам вообще не нужно вызывать ProcessMessages ().

Однако, если вы должны выполнить цикл в основном потоке, вы можете использовать MsgWaitForMultipleObject (), чтобы определить, когда вызывать ProcessMessages (), то есть:

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if MsgWaitForMultipleObjects(0, nil, False, 0, QS_ALLINPUT) = WAIT_OBJECT_0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

В качестве альтернативы с PeekMessage ():

var Msg: TMsg;

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

В качестве альтернативы с GetQueueStatus ():

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if GetQueueStatus(QS_ALLINPUT) <> 0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 
2 голосов
/ 04 августа 2010

Я бы также проголосовал за тему или что-то вроде Anreas AsyncCalls .Чтобы запретить пользователю выполнять какие-либо недопустимые действия в течение необходимого времени, вы можете установить флаг при запуске подпрограммы и сбросить ее по окончании (в любом случае вы должны обновить Screen.Cursor).Основной поток может проверить этот флаг и отключить все затронутые действия в своем событии OnUpdate.

1 голос
/ 04 августа 2010

Один вопрос, который необходимо решить, заключается в том, может ли ваше приложение продолжить работу, прежде чем вы получите ответ на любой цикл, который вычисляется.Если это невозможно, то нет особого смысла в том, чтобы приложение было «отзывчивым».Если вы пытаетесь обновить индикатор выполнения или что-то еще, вы можете вызывать .Repaint для элемента управления, содержащего индикатор выполнения, через каждое определенное число итераций, чтобы индикатор выполнения перерисовывался.

Если приложение можно продолжить, вхотя бы на некоторое время, тогда размещение кода в потоке - хорошая идея.

Помещение зацикливания кода в поток, вероятно, в любом случае целесообразно, особенно если вы хотите сделать что-то вроде прерывания обработки.Если вы никогда ранее не использовали потоки, есть некоторая кривая обучения, но для простого цикла, как вы описали, есть много примеров в Интернете.

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