Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > TThread + WaitForMultipleObjects


Автор: Dom 6.11.2009, 13:33
Столкнулся с тем что не могу понять как работает WaitForMultipleObjects. А может дело и не в нем.

Есть такой пример, большая часть кода позаимствована http://forum.vingrad.ru/index.php?showtopic=216315&view=findpost&p=1573798 smile

Код

unit Unit1;

interface

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

type
  TForm34 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    btn3: TButton;
    procedure btn1Click(Sender: TObject);
    procedure btn2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;

  TMy2Thread = class(TThread)
  private
    procedure Show1;
    procedure Show2;
  protected
    procedure Execute; override;
  end;

var
  Form34: TForm34;
  A: Integer = 0;

implementation
{$R *.dfm}
{ TMyThread }

procedure TMyThread.Execute;
begin
  Sleep(50);
  InterlockedIncrement(A);
  Sleep(1000);
end;

procedure TForm34.btn1Click(Sender: TObject);
var
  Handles: array of THandle;
  I: Integer;
  WaitRes: Cardinal;
begin
  SetLastError(NO_ERROR);
  SetLength(Handles, 50);
  A := 0;
  for I := Low(Handles) to High(Handles) do
    with TMyThread.Create(True) do
    begin
      Handles[I] := Handle;
      FreeOnTerminate := True;
      Resume;
    end;
  WaitRes := WaitForMultipleObjects(Length(Handles),
    @Handles, True, INFINITE);
  if WaitRes = WAIT_OBJECT_0 then
    ShowMessage(IntToStr(A))
  else
    ShowMessage(SysErrorMessage(GetLastError));
end;

procedure TForm34.btn2Click(Sender: TObject);
begin
{ чтобы не застывала главная форма выношу все в отдельный поток }
  TMy2Thread.Create(false).FreeOnTerminate := true; 
end;

{ TMy2Thread }

procedure TMy2Thread.Execute;
var
  Handles: array [1..MAXIMUM_WAIT_OBJECTS] of THandle;
  I: Integer;
  WaitRes: Cardinal;
begin
  SetLastError(NO_ERROR);
  A := 0;
  for I := Low(Handles) to High(Handles) do
    with TMyThread.Create(True) do
    begin
      Handles[I] := Handle;
      FreeOnTerminate := True;
      Resume;
    end;
  WaitRes := WaitForMultipleObjects(High(Handles),
    @Handles, True, INFINITE);
  if WaitRes = WAIT_OBJECT_0 then
    Synchronize(Show1)
  else
    Synchronize(Show2);
end;

procedure TMy2Thread.Show1;
begin
  ShowMessage(IntToStr(A))
end;

procedure TMy2Thread.Show2;
begin
  ShowMessage(SysErrorMessage(GetLastError));
end;

end.

Так вот процедура btn2Click отрабатывает как и предполагается, а вот btn1Click выдает ошибку "Неверный дескриптор".
1) Почему не получается обработать массив хэндлов произвольного размера, т.е. фактически произвольно задавать число создаваемых потоков?
2) Если после нажатия на btn2, пока не успели завершится все потоки, нажать на btn1, то выводимое значение А будет 50, т.е. отрабатывают только 50 потоков. Т.е. получается, что массивы Handles хоть и объявляются в каждой процедуре локально, но взаимосвязаны? Почему так? Пробовал переименовывать эти массивы, результат тот же.

Чувствую что истина где-то рядом... Помогите пожалуйста. smile

Автор: CodeMonkey 6.11.2009, 16:00
Чему, по-вашему, равно @Handles для http://transl-gunsmoker.blogspot.com/2009/09/blog-post.html#references?

Автор: Демо 6.11.2009, 22:29
Если хочешь использовать функции WaitFor..., то FreeOnTerminate := False. Только не забывай для своих классов-потоков выполнять Free;

Автор: CodeMonkey 6.11.2009, 23:36
Цитата(Демо @  6.11.2009,  22:29 Найти цитируемый пост)
Если хочешь использовать функции WaitFor..., то FreeOnTerminate := False.

Кста, http://transl-gunsmoker.blogspot.com/2009/06/blog-post_28.html (у вас WaitFor = использование, завершение потока = закрытие).

(Демо, это не вам, а автору вопроса)

Автор: Dom 6.11.2009, 23:56
CodeMonkey, спасибо за ссылки!
С хэндлами, точнее с динамическим массивом разобрался. А вот с WaitFor это уже на свежую голову...
Только почему же в оригинальном примере FreeOnTerminate := True
Ладно, это уже завтра.

Автор: Dom 7.11.2009, 15:25
Так вот, возвращаясь к напечатанному. По поводу WaitFor...
FreeOnTerminate := False надо ставить, чтобы просто наверняка исключить ситуацию, когда поток завершится раньше вызова WaitFor... или с какой-то иной целью?

Автор: Демо 8.11.2009, 04:12
Цитата(Dom @  7.11.2009,  15:25 Найти цитируемый пост)
Так вот, возвращаясь к напечатанному. По поводу WaitFor...
FreeOnTerminate := False надо ставить, чтобы просто наверняка исключить ситуацию, когда поток завершится раньше вызова WaitFor... или с какой-то иной целью?


С FreeOnTerminate=True ситуация следующая:

При завершении поточной поточной функции в основном потоке немедленно происходит освобождение TThread. 
Причём происходит оно раньше, чем начинает выполняться WaitFor.

Поэтому при ожидании завершения выполнения поточной функции нужно обязательно ставить FreeOnTerminate=False.

Автор: Akella 8.11.2009, 10:59
Вот рабочий пример
http://forum.vingrad.ru/index.php?showtopic=278503&view=findpost&p=2015045

Автор: Dom 9.11.2009, 23:53
Спасибо всем за помощь!
Собрал еще одну тестовую программку. Работает как и задумывалось, что не может не радовать. Можно сказать основы усвоил. Теперь дело за малым - переписать старую прогу под многопоточность.

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