Проблема С Кодом. Помогите

28.06.2012
19
0
#1
Нашел следующую справку вроде все просто

Waiting for a task to be completed

Sometimes, you need to wait for a thread to finish some operation rather than
waiting for a particular thread to complete execution. To do this, use an event object.
Event objects (TEvent) should be created with global scope so that they can act like
signals that are visible to all threads.
When a thread completes an operation that other threads depend on, it calls
TEvent.SetEvent. SetEvent turns on the signal, so any other thread that checks will
know that the operation has completed. To turn off the signal, use the ResetEvent
method.
For example, consider a situation where you must wait for several threads to
complete their execution rather than a single thread. Because you don’t know which
thread will finish last, you can’t simply use the WaitFor method of one of the threads.
Instead, you can have each thread increment a counter when it is finished, and have
the last thread signal that they are all done by setting an event.


The following code shows the end of the OnTerminate event handler for all of the
threads that must complete. CounterGuard is a global critical section object that
prevents multiple threads from using the counter at the same time. Counter is a global
variable that counts the number of threads that have completed.

procedure TDataModule.TaskThreadTerminate(Sender: TObject);
begin
ƒ
CounterGuard.Acquire; { obtain a lock on the counter }
Dec(Counter); { decrement the global counter variable }
if Counter = 0 then
Event1.SetEvent; { signal if this is the last thread }
CounterGuard.Release; { release the lock on the counter }
ƒ
end;
The main thread initializes the Counter variable, launches the task threads, and waits
for the signal that they are all done by calling the WaitFor method. WaitFor waits for a
specified time period for the signal to be set, and returns one of the values from Table

The following shows how the main thread launches the task threads and then
resumes when they have all completed:

Event1.ResetEvent; { clear the event before launching the threads }
for i := 1 to Counter do
TaskThread.Create(False); { create and launch task threads }
if Event1.WaitFor(20000) <> wrSignaled then
raise Exception;
{ now continue with the main thread. All task threads have finished }



Note If you do not want to stop waiting for an event after a specified time period, pass the
WaitFor method a parameter value of INFINITE. Be careful when using INFINITE,
because your thread will hang if the anticipated signal is never received.



Это уже мой код

unit Unit1;

interface

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

type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure DoWork1(Sender: TObject);
procedure FormCreate(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
end;
TMyThread1 = class(TThread)
private
{ Private declarations }
protected
procedure DoWork;
procedure Execute; override;
end;

TMyThread2 = class(TThread)
private
{ Private declarations }
protected
procedure DoWork;

procedure Execute; override;
end;

var
Form1: TForm1;
T1 : TMyThread1;
T2 : TMyThread2;
event:tevent;
Counter:integer;
CounterGuard:TCriticalSection;
implementation

{$R *.dfm}
procedure TMyThread1.Execute;
begin

Synchronize(DoWork);
end;

procedure TMyThread2.Execute;
begin
Synchronize(DoWork);
end;

procedure TMyThread1.DoWork;

begin
Form1.CheckBox1.Checked := false;

end;

procedure TMyThread2.DoWork;
begin
Form1.CheckBox1.Checked := false;

end;

procedure Tform1.DoWork1(Sender: TObject);
begin


CounterGuard.Acquire; { obtain a lock on the counter }
Dec(Counter); { decrement the global counter variable }
if Counter = 0 then
event.SetEvent; { signal if this is the last thread }
CounterGuard.Release; { release the lock on the counter }

end;
procedure TForm1.Button1Click(Sender: TObject);
begin


Event.ResetEvent;
T2 := TMyThread2.Create(False);
T1 := TMyThread1.Create(False);
T2.OnTerminate:=DoWork1;
T1.OnTerminate:=DoWork1;



if event.WaitFor(infinite)=wrSignaled then
showmessage('done');


Button1.Caption := 'Stop';

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
event:=tevent.Create(nil,true,true,'');

Counter:=2;
end;

initialization

CounterGuard:=Tcriticalsection.Create;
end.


Но этот код не работает. я что нибудь сделал не так?. Помогите. Он не сигналит когда потоки прекращают работать
 

sinkopa

Well-known member
17.06.2009
344
4
#2
Нашел следующую справку вроде все просто
...
Но этот код не работает. я что нибудь сделал не так?. Помогите. Он не сигналит когда потоки прекращают работать
Что значит "не сигналит"?
Вы можете объяснить человеческим языком, что Вы ожидаете от программы (как Вам кажется, что должно происходить?) в процессе выполнения кода который Вы изложили?
 
28.06.2012
19
0
#3
Но все же ясно, после того как два потока завершают работу они устанавливают событие в dowork1, которое должно уловить waitfor в главном потоке т.е в коде кнопки после чего появляется сообщение "done" но этого не происходит. Что не так ?
 

sinkopa

Well-known member
17.06.2009
344
4
#4
Но все же ясно, после того как два потока завершают работу они устанавливают событие в dowork1, которое должно уловить waitfor в главном потоке т.е в коде кнопки после чего появляется сообщение "done" но этого не происходит. Что не так ?
Ясно как раз, что Вы не прочитали справку по TThread.
constructor Create(CreateSuspended: Boolean);
Description
Call Create to create a thread in an application. If CreateSuspended is False, Execute is called immediately. If CreateSuspended is True, Execute won’t be called until after Resume is called.
Код:
{1} T1 := TMyThread1.Create(False);
//...
{2} T1.OnTerminate:=DoWork1;
Сточка {1} говорит о том что нить должна стартовать сразу же (после завершения конструктора Create).
Т.е. можете быть уверены, что к моменту строчки {2} Ваша нить уже отработала... :(
Если Вы хотите "успеть" назначить обработчик OnTerminate, то сначала его надо назначить а уж потом стартовать нить...
Код:
 T1 := TMyThread1.Create(True);
T1.OnTerminate := DoWork1;
T1.Resume;
Но мне все равно, извините, не понятен смысл Вашего кода...
Ведь DoWork1 все равно будет выполняться в главном потоке (после того как нити отработают)...
 
28.06.2012
19
0
#5
Dowork1 вызывается из моего кода правильно, я проверял, вы тоже можете проверить вставьте showmessage в dowork1 и вы увидите что его вызывают два раза. И событие тоже устанавливается но почемуто waitfor не видит его. А от того главный это поток или нет суть не меняется, все сообщение не выходит.
 

sinkopa

Well-known member
17.06.2009
344
4
#6
Dowork1 вызывается из моего кода правильно, я проверял, вы тоже можете проверить вставьте showmessage в dowork1 и вы увидите что его вызывают два раза. И событие тоже устанавливается но почемуто waitfor не видит его. А от того главный это поток или нет суть не меняется, все сообщение не выходит.
Еще раз Вам повторяю... DoWork1 должен выполняться (исходя из вышего кода) в ОСНОВНОМ ПОТОКЕ!
Но он (основной поток) не может этого сделать, потому как "висит" на ожидании Вашего Event.WaitFor(infinite)...
Разве не очевидно? :)
Поэтому в коде Вашем смысла я не углядел...
Либо Вынесите Event.WaitFor(infinite)... в отдельную нить, либо DoWork1 выполняйте непосредственно в теле (T1, T2).
Если Вам нужно чтобы Dec(Counter); происходил по завершении нити, включите код из DoWork1 в конец тела Execute, либо переопределите сам DoTerminate (как Вы сделали с методом Execute).
 
28.06.2012
19
0
#7
Я попробовал сделать как вы мне сказали но все равно не выходит. Может вы перепишите мой код и выложите правильный вариант, там всего несколько строк, а я протестирую,ведь задача вполне обычная, как узнать что потоки завершили работу не напрягая ресурсы системы, я уверен любой серьезный программист сталкивался с подобный проблемой.
 

sinkopa

Well-known member
17.06.2009
344
4
#8
Может вы перепишите мой код и выложите правильный вариант...
Это называется "пойди туда, не знаю куда"... :blink:
Скажите, как можно исправить то, смысл чего не понимаешь?
Вы же так и не рассказали, чего Вы добиваетесь от своего кода (алгоритм работы)...

Ок, раз я сегодня "дежурный экстрасенс", осмелюсь предположить что Вы хотели что нибуть типа этого
Код:
unit Unit1;

interface

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

type
TMainThread = class(TThread)
protected
procedure Execute; override;
public
StopMSG : string;
MSGWindow: HWND;
end;

TMyThread1 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;

TMyThread2 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;

type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
procedure ResetButton(Sender: TObject);
{ Public declarations }
end;


var
Form1: TForm1;

{ Рабочий поток (нить) 1 }
T1 : TMyThread1;
{ Рабочий поток (нить) 2 }
T2 : TMyThread2;

{ Поток (нить) запускающий нити 1,2
и ожидающий событие Event }
MainThread : TMainThread;

{ Событие, которое установит на DoTerminate одна из рабочих нитей
либо пользователь (кнопкой Stop, если ему надоест ждать) }
Event: TEvent;

{ Счетчик, который декрементят рабочие нити }
Counter:integer;

{ Критическая секция, не дающая рабочим нитям "драться" }
CounterGuard: TCriticalSection;

implementation

{$R *.dfm}


{ TMyThread1 }

procedure TMyThread1.DoWork;
begin
Form1.CheckBox1.Checked := not Form1.CheckBox1.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;

procedure TMyThread1.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"

if (Counter < 1 ) then
Break;
end;
end;

procedure TMyThread1.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;


{ TMyThread2 }

procedure TMyThread2.DoWork;
begin
Form1.CheckBox2.Checked := not Form1.CheckBox2.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;

procedure TMyThread2.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"

if (Counter < 1 ) then
Break;
end;
end;

procedure TMyThread2.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;


{ TMainThread }

procedure TMainThread.Execute;
begin
// Запустили рабочие нити
T1.Resume;
T2.Resume;

// Ожидаем рождения события.
// !!!! Внимание !!! Управление в поток вернется ТОЛЬКО после получения результата WaitFor !!!
// А до тех пор, этот поток (нить) будет находитьс в "записшем" состоянии

if (Event.WaitFor(INFINITE) <> wrSignaled) then // События не дожналить
MessageBox(0, 'Ошибка!', PChar(Application.Title), MB_OK + MB_ICONSTOP +
MB_TOPMOST)
else // Событие родилось
MessageBox(MSGWindow, PChar(StopMSG), PChar(Application.Title), MB_OK +
MB_ICONINFORMATION + MB_TOPMOST);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
// Рождение критической секции
CounterGuard := Tcriticalsection.Create;

// Рождение экземпляра TEvent
Event := TEvent.Create(nil,true,true,'');
ResetButton(Self);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if (Button1.Caption = 'Stop') then // Остановка вручную (если нам надоело ждать)
begin
// Останавливаем рабочие нити
T1.Terminate;
T2.Terminate;

// Ждем их фактической остановки
T1.WaitFor;
T2.WaitFor;

MainThread.StopMSG := 'Остановлено пользователем';

// Вручную устанавливаем событие
Event.SetEvent;

// Ждем завершения MainThread
MainThread.WaitFor;

// Освобождаем память
MainThread.Free;
T1.Free;
T2.Free;
end
else					 // Запуск "системы"
begin
// Устанавливаем счетчик
Counter := 100;
// Сбрасываем (тушим) событие
Event.ResetEvent;

// Рождаем рабочие нити
T2 := TMyThread2.Create(True);
T1 := TMyThread1.Create(True);

// Рождаем поток - ловец события
MainThread := TMainThread.Create(True);
MainThread.StopMSG := 'Готово!'#13+'Сигнал получен!';
MainThread.MSGWindow := Form1.Handle;
MainThread.OnTerminate := ResetButton;

// Запускаем "Систему"
MainThread.Resume;

Button1.Caption := 'Stop';
end;

end;


procedure TForm1.ResetButton(Sender: TObject);
begin
Caption := 'Готов к работе';
Button1.Caption := 'Start';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Event.Free;
CounterGuard.Free;
end;

end.
Ну и тогда уж, вот Вам исходники:
Посмотреть вложение ThreadTest.rar
 

Вложения

28.06.2012
19
0
#9
Браво я протестировал ваш пример, все работает, я например обращался на западные форумы , например вот здесь http://codeby.net/forum/threads/47530.html, но никто помочь мне не смог
Похоже на этом форуме настоящие программисты.
Я упростил ваш пример и все работает но вот почему сообщения ”Yes, it works”
которое вызывается после завершения потоков, почему выходит то в нормальной форме, то в слишком большом виде, а почему так происходит не понятно.

unit Unit1;

interface

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

type
TMainThread = class(TThread)
protected
procedure Execute; override;
public

end;

TMyThread1 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;

TMyThread2 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;

type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
procedure ResetButton(Sender: TObject);
{ Public declarations }
end;


var
Form1: TForm1;

T1 : TMyThread1;
T2 : TMyThread2;

MainThread : TMainThread;

Event: TEvent;


Counter:integer;

CounterGuard: TCriticalSection;

implementation

{$R *.dfm}


{ TMyThread1 }

procedure TMyThread1.DoWork;
begin


end;

procedure TMyThread1.Execute;
begin
{ some code here }

end;

procedure TMyThread1.DoTerminate;
begin

CounterGuard.Acquire;
Dec(Counter);
if (Counter = 0) then
Event.SetEvent;


CounterGuard.Release;
inherited;
end;




procedure TMyThread2.DoWork;
begin


end;

procedure TMyThread2.Execute;
begin
{ some code here }

end;

procedure TMyThread2.DoTerminate;
begin

CounterGuard.Acquire;
Dec(Counter);
if (Counter = 0) then
Event.SetEvent;


CounterGuard.Release;
inherited;
end;


{ TMainThread }

procedure TMainThread.Execute;
begin

T1.Resume;
T2.Resume;



if (Event.WaitFor(INFINITE) = wrSignaled) then
showmessage('Yes, it works');
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
CounterGuard := Tcriticalsection.Create;

Event := TEvent.Create(nil,true,true,'');
counter:=2;
ResetButton(Self);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if (Button1.Caption = 'Stop') then
begin

T1.Terminate;
T2.Terminate;


MainThread.Free;
T1.Free;
T2.Free;
end
else
begin

Event.ResetEvent;


T2 := TMyThread2.Create(True);
T1 := TMyThread1.Create(True);


MainThread := TMainThread.Create(True);

MainThread.OnTerminate := ResetButton;


MainThread.Resume;

Button1.Caption := 'Stop';
end;

end;


procedure TForm1.ResetButton(Sender: TObject);
begin
Caption := 'Готов к работе';
Button1.Caption := 'Start';

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
Event.Free;
CounterGuard.Free;
end;

end.
 

sinkopa

Well-known member
17.06.2009
344
4
#10
Браво я протестировал ваш пример, все работает
Не вижу причин, почему это не должно было работать...
Я упростил ваш пример и все работает но вот почему сообщения ”Yes, it works”
которое вызывается после завершения потоков, почему выходит то в нормальной форме, то в слишком большом виде, а почему так происходит не понятно.
Советую научиться пользоваться кнопочкой F1...
Если вы почитаете справку, то узнаете, что ShowMessage это диалог который "растёт" от TApplication, который в свою очередь есть главный поток. А всё что связано с формами главного потока, в параллельных нитях следует выполнять через Synchronize. Иначе... ну результат Вы видели... :blink:
Если Вы обратили внимание, для вывода сообщений в параллельных нитях, я использовал MessageBox... который есть WinAPI...
Большая просьба, когда постите код, включайте его пожалуйста в мнемотэг code... это такая кнопочка с надписью PAS над окошком поста (4-я справа)... :)
А то очень уж тяжело читать
Спасибо за понимание...

PS.
Да... вот еще что... Я тут посмотрел еще раз Ваш измененный код...
У меня есть некоторые замечания...
1. Вот эти строчки
Код:
T1.Terminate;
T2.Terminate;
вовсе не означают, что T1 и T2 тут же умрут. Метод Terminate всего лишь выставляют приватное поле нити FTerminated в состояние True
Отсюда следует:
а). После T2.Terminate; обязательно должна стоять строчка T1.WaitFor; иначе может получиться... не то что хотелось бы.
б). Параллельная нить должна сама проверять "не пора ли ей умереть"... Для этого внутри Вашего { some code here } , причем (если это не цикл коротких операций) во многих местах.

2. MainThread.Free; до того как поток терминирован - плохая идея... Имейте в виду - Эта команда разрушит Экземпляр класса TMainThread но вовсе не саму нить... :)
Я даже боюсь гадать, что может произойти, когда ничего не подозревающая (о кончине класса-обертки) нить, после штатного (как ей самой кажется) завершения, попытается задейтвовать области памяти, которые Вы так необдуманно освободили деструктором MainThread...
 
28.06.2012
19
0
#11
// Запускаем "Систему"
MainThread.Resume;
// В вашем примере как заставить поток остановиться и дождаться завершения MainThread //а потом уже устанавливать свойство СТОП в названия кнопки. Т.Е задача такая основной //поток запускает 15 потоков и ждет завершения всех 15 поток а потом продолжает //работать или проще как заставить поток остановитья на этом самом месте и дождавшись //завершения майнсреда установить капшн СТОП
Button1.Caption := 'Stop';
 

sinkopa

Well-known member
17.06.2009
344
4
#12
// Запускаем "Систему"
MainThread.Resume;
// В вашем примере как заставить поток остановиться и дождаться завершения MainThread //а потом уже устанавливать свойство СТОП в названия кнопки. Т.Е задача такая основной //поток запускает 15 потоков и ждет завершения всех 15 поток а потом продолжает //работать или проще как заставить поток остановитья на этом самом месте и дождавшись //завершения майнсреда установить капшн СТОП
Button1.Caption := 'Stop';
Код:
procedure TMainThread.DisableButton;
begin
  FStopButton.Caption := 'Wait...';
  FStopButton.Enabled := False;
end;
 
procedure TMainThread.EnableButton;
begin
  FStopButton.Caption := 'Stop';
  FStopButton.Enabled := True;
end;
 
procedure TMainThread.Execute;
var
isDone: Boolean;
begin
  Synchronize(DisableButton);
  ChildThread1.Resume;
  ChildThread2.Resume;
//  ChildThread3.Resume;
// ...
  ChildThread14.Resume;
  ChildThread15.Resume;
 
  isDone := ChildThread1.Terminated and ChildThread2.Terminated and
			// ChildThread3.Terminated and ChildThread4.Terminated and
			// ...
			ChildThread14.Terminated and ChildThread15.Terminated;
 
  while not isDone do begin
	Sleep(20);
  isDone := ChildThread1.Terminated and ChildThread2.Terminated and
			// ChildThread3.Terminated and ChildThread4.Terminated and
			// ...
			ChildThread14.Terminated and ChildThread15.Terminated;
 
  end;
 
  Synchronize(EnableButton);
  // bla bla
end;
 
28.06.2012
19
0
#13
Здравствуйте вы не отстанавливайте поток в основном коде (коде кнопки), кнопка по прежнему получает капш стоп в тот самый момент пока работают дочерние потоки, вы изменяете капшн из дочених потоков, а у меня задача совсем другая предположим кнопка вызавает дочерние потоки а после MainThread.Resume; у меня продолжается код который продолжает испольняться во время работы дочерниех потоков не дождавшись из завершения, а мне нужно чтоб код после MainThread.Resume; испольнялся после завершения дочерних потоков. Пример
Код:
unit Unit1;
 
interface
 
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, SyncObjs, StdCtrls;
 
type
TMainThread = class(TThread)
protected
procedure Execute; override;
public
StopMSG : string;
MSGWindow: HWND;
end;
 
TMyThread1 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;
 
TMyThread2 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;
 
type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
procedure ResetButton(Sender: TObject);
{ Public declarations }
end;
 
 
var
Form1: TForm1;
 
{ Рабочий поток (нить) 1 }
T1 : TMyThread1;
{ Рабочий поток (нить) 2 }
T2 : TMyThread2;
 
{ Поток (нить) запускающий нити 1,2
и ожидающий событие Event }
MainThread : TMainThread;
 
{ Событие, которое установит на DoTerminate одна из рабочих нитей
либо пользователь (кнопкой Stop, если ему надоест ждать) }
Event: TEvent;
 
{ Счетчик, который декрементят рабочие нити }
Counter:integer;
 
{ Критическая секция, не дающая рабочим нитям "драться" }
CounterGuard: TCriticalSection;
 
implementation
 
{$R *.dfm}
 
 
{ TMyThread1 }
 
procedure TMyThread1.DoWork;
begin
Form1.CheckBox1.Checked := not Form1.CheckBox1.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;
 
procedure TMyThread1.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
 
if (Counter < 1 ) then
Break;
end;
end;
 
procedure TMyThread1.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;
 
 
{ TMyThread2 }
 
procedure TMyThread2.DoWork;
begin
Form1.CheckBox2.Checked := not Form1.CheckBox2.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;
 
procedure TMyThread2.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
 
if (Counter < 1 ) then
Break;
end;
end;
 
procedure TMyThread2.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;
 
 
{ TMainThread }
 
procedure TMainThread.Execute;
begin
// Запустили рабочие нити
T1.Resume;
T2.Resume;
 
// Ожидаем рождения события.
// !!!! Внимание !!! Управление в поток вернется ТОЛЬКО после получения результата WaitFor !!!
// А до тех пор, этот поток (нить) будет находитьс в "записшем" состоянии
 
if (Event.WaitFor(INFINITE) <> wrSignaled) then // События не дожналить
MessageBox(0, 'Ошибка!', PChar(Application.Title), MB_OK + MB_ICONSTOP +
MB_TOPMOST)
else // Событие родилось
MessageBox(MSGWindow, PChar(StopMSG), PChar(Application.Title), MB_OK +
MB_ICONINFORMATION + MB_TOPMOST);
end;
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
// Рождение критической секции
CounterGuard := Tcriticalsection.Create;
 
// Рождение экземпляра TEvent
Event := TEvent.Create(nil,true,true,'');
ResetButton(Self);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
if (Button1.Caption = 'Stop') then // Остановка вручную (если нам надоело ждать)
begin
// Останавливаем рабочие нити
T1.Terminate;
T2.Terminate;
 
// Ждем их фактической остановки
T1.WaitFor;
T2.WaitFor;
 
MainThread.StopMSG := 'Остановлено пользователем';
 
// Вручную устанавливаем событие
Event.SetEvent;
 
// Ждем завершения MainThread
MainThread.WaitFor;
 
// Освобождаем память
MainThread.Free;
T1.Free;
T2.Free;
end
else					 // Запуск "системы"
begin
// Устанавливаем счетчик
Counter := 100;
// Сбрасываем (тушим) событие
Event.ResetEvent;
 
// Рождаем рабочие нити
T2 := TMyThread2.Create(True);
T1 := TMyThread1.Create(True);
 
// Рождаем поток - ловец события
MainThread := TMainThread.Create(True);
MainThread.StopMSG := 'Готово!'#13+'Сигнал получен!';
MainThread.MSGWindow := Form1.Handle;
MainThread.OnTerminate := ResetButton;
 
// Запускаем "Систему"
MainThread.Resume;
 Button1.Caption := 'Stop';
showmessage('Main thread finished its work');
//сделайте пожалуйста так чтоб это сообщения вызывалось после "сигнал получен"
end;
 
end;
 
 
procedure TForm1.ResetButton(Sender: TObject);
begin
Caption := 'Готов к работе';
Button1.Caption := 'Start';
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
Event.Free;
CounterGuard.Free;
end;
 
end.
Посмотрите на шоумессадж после MainThread.Resume; Сделайте так чтоб исходя из вашего примера сначала завершил работу Mainthread а потом вызывалось шоумессадж. я пробовал WaitForSingleObject, но он не сработал можно побровать использовать таймер который будет проверять значения boolean который станет true когда получет сигнал но это както не изящно.
[DOUBLEPOST=1450008304,1450008293][/DOUBLEPOST]Здравствуйте вы не отстанавливайте поток в основном коде (коде кнопки), кнопка по прежнему получает капш стоп в тот самый момент пока работают дочерние потоки, вы изменяете капшн из дочених потоков, а у меня задача совсем другая предположим кнопка вызавает дочерние потоки а после MainThread.Resume; у меня продолжается код который продолжает испольняться во время работы дочерниех потоков не дождавшись из завершения, а мне нужно чтоб код после MainThread.Resume; испольнялся после завершения дочерних потоков. Пример
Код:
unit Unit1;
 
interface
 
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, SyncObjs, StdCtrls;
 
type
TMainThread = class(TThread)
protected
procedure Execute; override;
public
StopMSG : string;
MSGWindow: HWND;
end;
 
TMyThread1 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;
 
TMyThread2 = class(TThread)
protected
procedure DoTerminate; override;
procedure DoWork;
procedure Execute; override;
end;
 
type
TForm1 = class(TForm)
Button1: TButton;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
procedure ResetButton(Sender: TObject);
{ Public declarations }
end;
 
 
var
Form1: TForm1;
 
{ Рабочий поток (нить) 1 }
T1 : TMyThread1;
{ Рабочий поток (нить) 2 }
T2 : TMyThread2;
 
{ Поток (нить) запускающий нити 1,2
и ожидающий событие Event }
MainThread : TMainThread;
 
{ Событие, которое установит на DoTerminate одна из рабочих нитей
либо пользователь (кнопкой Stop, если ему надоест ждать) }
Event: TEvent;
 
{ Счетчик, который декрементят рабочие нити }
Counter:integer;
 
{ Критическая секция, не дающая рабочим нитям "драться" }
CounterGuard: TCriticalSection;
 
implementation
 
{$R *.dfm}
 
 
{ TMyThread1 }
 
procedure TMyThread1.DoWork;
begin
Form1.CheckBox1.Checked := not Form1.CheckBox1.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;
 
procedure TMyThread1.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
 
if (Counter < 1 ) then
Break;
end;
end;
 
procedure TMyThread1.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;
 
 
{ TMyThread2 }
 
procedure TMyThread2.DoWork;
begin
Form1.CheckBox2.Checked := not Form1.CheckBox2.Checked;
Form1.Caption := Format('Counter = %d (Поток %s)',[Counter,Self.ClassName]);
Form1.Refresh;
end;
 
procedure TMyThread2.Execute;
begin
{ Крутимся в цикле, пока счетчик не обнулится или нас не "терминируют" }
while (not Terminated) do
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
Sleep(100);
Synchronize(DoWork);
Dec(Counter);
CounterGuard.Release; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
 
if (Counter < 1 ) then
Break;
end;
end;
 
procedure TMyThread2.DoTerminate;
begin
CounterGuard.Acquire; // Заняли секцию. Пока мы в ней, "никто другой не войдет"
if (Counter < 1) then // Если счетчик обнулился, "рождаем событие"
Event.SetEvent;
CounterGuard.Release; // Освободили секцию.
inherited;
end;
 
 
{ TMainThread }
 
procedure TMainThread.Execute;
begin
// Запустили рабочие нити
T1.Resume;
T2.Resume;
 
// Ожидаем рождения события.
// !!!! Внимание !!! Управление в поток вернется ТОЛЬКО после получения результата WaitFor !!!
// А до тех пор, этот поток (нить) будет находитьс в "записшем" состоянии
 
if (Event.WaitFor(INFINITE) <> wrSignaled) then // События не дожналить
MessageBox(0, 'Ошибка!', PChar(Application.Title), MB_OK + MB_ICONSTOP +
MB_TOPMOST)
else // Событие родилось
MessageBox(MSGWindow, PChar(StopMSG), PChar(Application.Title), MB_OK +
MB_ICONINFORMATION + MB_TOPMOST);
end;
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
// Рождение критической секции
CounterGuard := Tcriticalsection.Create;
 
// Рождение экземпляра TEvent
Event := TEvent.Create(nil,true,true,'');
ResetButton(Self);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
if (Button1.Caption = 'Stop') then // Остановка вручную (если нам надоело ждать)
begin
// Останавливаем рабочие нити
T1.Terminate;
T2.Terminate;
 
// Ждем их фактической остановки
T1.WaitFor;
T2.WaitFor;
 
MainThread.StopMSG := 'Остановлено пользователем';
 
// Вручную устанавливаем событие
Event.SetEvent;
 
// Ждем завершения MainThread
MainThread.WaitFor;
 
// Освобождаем память
MainThread.Free;
T1.Free;
T2.Free;
end
else					 // Запуск "системы"
begin
// Устанавливаем счетчик
Counter := 100;
// Сбрасываем (тушим) событие
Event.ResetEvent;
 
// Рождаем рабочие нити
T2 := TMyThread2.Create(True);
T1 := TMyThread1.Create(True);
 
// Рождаем поток - ловец события
MainThread := TMainThread.Create(True);
MainThread.StopMSG := 'Готово!'#13+'Сигнал получен!';
MainThread.MSGWindow := Form1.Handle;
MainThread.OnTerminate := ResetButton;
 
// Запускаем "Систему"
MainThread.Resume;
 Button1.Caption := 'Stop';
showmessage('Main thread finished its work');
//сделайте пожалуйста так чтоб это сообщения вызывалось после "сигнал получен"
end;
 
end;
 
 
procedure TForm1.ResetButton(Sender: TObject);
begin
Caption := 'Готов к работе';
Button1.Caption := 'Start';
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
Event.Free;
CounterGuard.Free;
end;
 
end.
Посмотрите на шоумессадж после MainThread.Resume; Сделайте так чтоб исходя из вашего примера сначала завершил работу Mainthread а потом вызывалось шоумессадж. я пробовал WaitForSingleObject, но он не сработал можно побровать использовать таймер который будет проверять значения boolean который станет true когда получет сигнал но это както не изящно.