Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Как ждать завершения функции выполняемой в потоке?


Автор: Caduser 15.1.2007, 07:16
Есть основная форма, в ней происходит вызов функции которая выполняется долго и естественно блокирует весь интерфейс. Задача: поместить функцию в поток, так чтобы основной интерфейс не блокировался НО ОЖИДАЛ окончания выполнения функции.
В основной форме:
Код

stResult := MyFunc1;
ShowMessage(stResult);

Описание потока и функции:
Код

  TMyThread = class(TThread)
  private
    //
  protected
    procedure Execute; override;
  public
     FstResult: string;
  end;

{ TMyThread }

procedure TMyThread.Execute;
begin
//< тело функции>
// что то долго выполняющееся
 FstResult := 'Результат';
end;

// описание функции
function MyFunc1: string
begin
  MyThread.Resume;
  MyThread.WaitFor;
  result := MyThread.FstResult;
end;

Этот вариант не годится, т.к. WaitFor опять таки все блокирует!
Как ожидать окончание выполнения функции?
(читал много мотериалов но описание подобного применения потоков не нашел)

Автор: aktuba 15.1.2007, 08:03
Код

stResult := MyFunc1;
while stResult = '' do Application.ProcessMessages;
ShowMessage(stResult);

Автор: Caduser 15.1.2007, 10:09
Код

while stResult = '' do Application.ProcessMessages;

Действительно работает. Однако не приводит ли этот цикл к дополнительной загрузке процессора (так мне показалось при тестировании)? Может есть какой-нибудь более эффективный способ (пусть даже не простой)?

Автор: aktuba 15.1.2007, 10:27
Нет, не приводит. Эта строка просто заставляет обрабатывать все сообщения.
Только учти один момент - если функция из потока вернет пустую строку, то код дальше исполняться не будет...

Автор: MetalFan 15.1.2007, 10:52
а не правильнее ли сделать "событие" на завершение работы потока и считать результат?!
просто в приведенном примере смысл отдельного потока теряется абсолютно. могли бы и в главном тоже самое делать.
как лучше сделать:
при старте доп.потока выставляете флаг в главном потоке, что идет обработка данных. по оконачнии работы доп.потока сбрасываете флаг и считывать результат.
ну или вместо флага просто можно выставлять доступность действий/контролов.

Автор: Snowy 15.1.2007, 11:03
Просто, при завершении работы треда, вызвать нужный метод синхронизацией

Автор: aktuba 15.1.2007, 11:44
MetalFan, Snowy, а какой смысл использовать поток в данной задаче? какой вопрос - такой ответ...

Автор: Snowy 15.1.2007, 12:03
Код

  TMyThread = class(TThread)
  protected
    procedure Execute; override;
    procedure OnEnd;
  public
     FstResult: string;
  end;

procedure TMyThread.Execute;
begin
//< тело функции>
// что то долго выполняющееся
 FstResult := 'Результат';
  Syncronize(OnEnd);
end;

// описание функции
procedure MyProc1;
begin
  MyThread.Resume;
end;

procedure TMyThread.OnEnd;
begin
  ShowMessage('Результат: ' + FstResult);
end;

Автор: Caduser 16.1.2007, 02:25
Цитата(MetalFan @ 15.1.2007,  10:52)
просто в приведенном примере смысл отдельного потока теряется абсолютно. могли бы и в главном тоже самое делать.
как лучше сделать:
при старте доп.потока выставляете флаг в главном потоке, что идет обработка данных. по оконачнии работы доп.потока сбрасываете флаг и считывать результат.

Уточняю суть задачи. Тело функции это запрос к базе через DCOM: 
Код

cds1.data := DCOM.AppServer.MyFunc1;

Выполнение этого запроса в главном потоке приводит к полной блокировке интерфейса (Application.ProcessMessages вставлять тут естественно некуда). Единственный выход - поместить DCOM.AppServer.MyFunc1 в отдельный поток, однако выполнение основной программы должно быть приостановлено до завершения выполнения этого потока. 

Автор: MetalFan 16.1.2007, 09:44
Цитата(Caduser @  16.1.2007,  02:25 Найти цитируемый пост)
выполнение основной программы должно быть приостановлено

так, тогда зачем еще один поток? у тебя и так "приостановится" главный поток, пока не выполнится данная функция ;)

наверное ты имел ввиду не полную "приостановку", а просто блокировку части GUI до завершения операции?
тогда правильнее, имхо, сделать нотфикацию форме после завершения выполнения функции в трэде... а перед созданием оного блокируй часть контролов.
это будет правильнее, чем крутиться в цикле с ProcessMessages, проверяя состояние доп.потока
в общем смотри, пример уже дали ;)

Автор: aktuba 16.1.2007, 09:57
MetalFan, ты не понял. Ему наоборот надо НЕ блокировать GUI, но ждать результата функции.
Я бы тоже не выносил в отдельный поток. Просто в выполняемой функции, занимающей долгое время, добавил ProcessMessages...

Автор: MetalFan 16.1.2007, 10:16
aktuba, это ты не понял. НЕКУДА ему добавлять ProcessMesages при выполнении функции в главном потоке)

Автор: CatATonik 16.1.2007, 10:53
Вот так, и процессор не грузится, только обязательно в конце нужно послать приложению какое либо сообщение (не обязательно WM_ACTIVATEAPP):

Код

procedure TMyForm.MyLongTimeProc;
begin
  stResult := '';
  MyThread := TMyThread.Create(False);
  while (stResult = '') and not Application.Terminated do
    Application.HandleMessage;
end;

procedure TMyThread.Execute;
begin
  try
    cds1.data := DCOM.AppServer.MyFunc1;
    stResult := 'Результат';
  finally
    SendMessage(Application.Handle, WM_ACTIVATEAPP, 1, 0);
  end;
end;

Автор: Caduser 22.1.2007, 04:38
Цитата(CatATonik @  16.1.2007,  10:53 Найти цитируемый пост)
Вот так, и процессор не грузится, только обязательно в конце нужно послать приложению какое либо сообщение (не обязательно WM_ACTIVATEAPP)

Отличное решение, все работает, спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)