Создание Потока (tthread)

Shouldercannon

Well-Known Member
25.05.2010
128
0
#1
Данный поток построен верно?
Код:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdHTTP;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure TNewThread_ThreadTerminate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TNewThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
NewThread: TNewThread;
begin
NewThread := TNewThread.Create(True); // Поток создан и остановлен
NewThread.FreeOnTerminate := True; // Уничтожить поток после завершения работы
NewThread.Resume; // Запуск потока
NewThread.OnTerminate := TNewThread_ThreadTerminate;
end;

procedure TNewThread.Execute;
var
HTTP: TIdHTTP;
TMS: TMemoryStream;
begin
HTTP := TIdHTTP.Create(nil);
TMS := TMemoryStream.Create;
try
try
HTTP.Get('http://img.yandex.net/i/www/logo.png', TMS);
TMS.SaveToFile('logo.png');
except
end;
finally
HTTP.Free;
TMS.Free;
end;
end;

procedure TForm1.TNewThread_ThreadTerminate(Sender: TObject);
begin
// Что-то
end;

end.
Как можно TNewThread_ThreadTerminate описать в потоке TNewThread = class(TThread), а не отдельно, если это вообще возможно?
 

sinkopa

Well-Known Member
17.06.2009
344
9
#2
Данный поток построен верно?
Почти... :rolleyes:
Код:
// Вот так. Сначала устанавливаем все свойства, а потом уже запускаем
procedure TForm1.Button1Click(Sender: TObject);
var
NewThread: TNewThread;
begin
NewThread := TNewThread.Create(True); // Поток создан и остановлен
NewThread.OnTerminate := TNewThread_ThreadTerminate; 
NewThread.FreeOnTerminate := True; // Уничтожить поток после завершения работы
NewThread.Resume; // Запуск потока
end;
Как можно TNewThread_ThreadTerminate описать в потоке TNewThread = class(TThread), а не отдельно, если это вообще возможно?
Если я правильно понял Ваш вопрос, то вот так
Код:
type
TNewThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure DoTerminate; override;
end;
//...
implementation

{ TNewThread }

procedure TNewThread.DoTerminate;
begin
// Что-то... Если нужно до вызова внешнего OnTerminate (если назначен)
inherited;
// Что-то... Если нужно после вызова внешнего OnTerminate (если назначен)
end;

procedure TNewThread.Execute;
var
HTTP: TIdHTTP;
TMS: TMemoryStream;
begin
HTTP := TIdHTTP.Create(nil);
TMS := TMemoryStream.Create;
try
try
HTTP.Get('http://img.yandex.net/i/www/logo.png', TMS);
TMS.SaveToFile('logo.png');
except
end;
finally
HTTP.Free;
TMS.Free;
end;
end;
 

Shouldercannon

Well-Known Member
25.05.2010
128
0
#3
1. В procedure TNewThread.DoTerminate; можно делать что угодно: обращаться к компонентам формы, вносить изменения в глобальные переменные, запускать другие приложения, закрывать формы и т.д.? ShowMessage там точно нельзя делать, окна получаются корявыми (либо огромные, либо широкие). Если нет, то где такое можно делать по завершению работы потока.
2. Если я в потоке вношу данные в Memo, ListView и прочие, то я должен производить синхронизацию с основным потоком?
3. Если я в потоке вношу изменения в глобальные переменные (string, Integer, Boolean и прочие), то я должен производить синхронизацию с основным потоком?
 

sinkopa

Well-Known Member
17.06.2009
344
9
#4
1. В procedure TNewThread.DoTerminate; можно делать что угодно:
Ну... лекцию я тут Вам читать не буду... курите Google.
Ключевые слова: Параллельные потоки, Критические секции (TCriticalSection) , Событие (TEvent), Мьютекс (mutex), семафор (semaphore)
Если коротко (и слегка утрируя) Всё можно... если осторожно... :newconfus:
обращаться к компонентам формы, вносить изменения в глобальные переменные, запускать другие приложения, закрывать формы и т.д.?
Несколько коротких примеров
1. Через Synchronize
Код:
uses
SyncObjs;
//...
type
TForm1 = class(TForm)
Memo1: TMemo;
private
//...
end;

type
TNewThread = class(TThread)
//...
protected
procedure UpdateForm1Data;
procedure DoTerminate; override;
end;

implementation

//...
procedure TNewThread.UpdateForm1Data;
begin
Form1.Memo1.Lines.Add('blabla');
Form1.Memo1.Refresh;
end;

procedure TNewThread.DoTerminate;
begin
Synchronize(UpdateForm1Data); // 1. Поток TNewThread приостанавливается. 2. UpdateForm1Data выполняется В ОСНОВНОМ потоке приложения
inherited;
end;
2. Через критическую секцию
Код:
uses
SyncObjs;
//...
var
Section1: TCriticalSection;

implementation
//...
{Когда поток TNewThread встретит в процедуре Section1.Enter, он проверит, не занята ли секция другим потоком и, если она свободна, займет ее и начнет выполнять код}
procedure TNewThread.DoTerminate;
begin
Section1.Enter;
Form1.Memo1.Lines.Add('blabla из TNewThread');
Form1.Memo1.Refresh;
Section1.Leave;
inherited;
end;
{Когда основной поток встретит в процедуре Section1.Enter, он проверит, не занята ли секция другим потоком и, если она свободна, займет ее и начнет выполнять код}
procedure TForm1.Button1Click(Sender: TObject);
begin
Section1.Enter;
Form1.Memo1.Lines.Add('blabla из основного потока');
Form1.Memo1.Refresh;
Section1.Leave;
end;
//...
initialization
Section1 := TCriticalSection.Create;
3. Через WaitFor
Код:
uses
SyncObjs;
//...
var
Event1: TEvent;
ClobalCounter: Int64;

implementation
//...
{Первый поток, желающий работать с переменной ClobalCounter, не будет остановлен методом WaitFor, так как изначально объект события был создан в активном состоянии. Далее произойдет сброс события, и поток "схатит за рога" ClobalCounter. Другой поток, будет приостановлен методом WaitFor, так как первый поток перевел объект события в пассивное состояние. Когда первый поток переведет событие в активное состояние, второй поток продолжит работу...}

procedure TNewThread.DoTerminate;
var
i: Integer;
begin
Event1.WaitFor(INFINITE);
Event1.ResetEvent;
for i :=1 to 10 do
Inc(ClobalCounter);
Event1.SetEvent;
inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Event1.WaitFor(INFINITE);
Event1.ResetEvent;
ShowMessage(IntToStr(ClobalCounter));
Event1.SetEvent;
end;
//...
initialization
Event1 := TEvent.Create(nil,False,True,'');
4. Через SendMessage
Код:
const
WM_MYUPDATEDATA = WM_USER + 105;

type
TForm1 = class(TForm)
//...
ProgressBar1: TProgressBar; 
private
procedure WmMyUpdateData(var msg: TMessage); message WM_MYUPDATEDATA;
public
{ Public declarations }
end;

type
TNewThread = class(TThread)
protected
//... 
procedure DoTerminate; override;
public
FormHandle: HWND;
end;

implementation
{Поток TNewThread шлет сообщение WM_MYUPDATEDATA окну FormHandle рожденному в основном потоке. "Слушальщик" WmMyUpdateData формы TForm1 принимает сообщение и обрабатывает в основном потоке }
procedure TNewThread.DoTerminate;
var
progress: Integer;
cap: string;
begin
progress := 100;
cap := 'blabla';
SendMessage(FormHandle,WM_MYUPDATEDATA, WParam(Pointer(PChar(cap))) LParam(progress), )
inherited;
end;

procedure TForm1.WmMyUpdateData(var msg: TMessage);
var
PCap: Pointer;
begin
if msg.Msg = WM_MYUPDATEDATA then
begin
Integer(PCap) := msg.WParam;
Caption := PChar(PCap);
ProgressBar1.Position := Integer(msg.LParam);
// msg.Result := ... //если нужна обратная связь
end;
end;
Есть и другие способы взаимодействия параллельных потоков... например таймеры...
ShowMessage там точно нельзя делать, окна получаются корявыми (либо огромные, либо широкие).
Хм.. Думаю не следует возводить частности в ранг постулатов... Если внимательно присмотреться к ShowMessage... выяснится что это на самом деле Диалог...
А диалоги вообще-то ориентированы на работе в главном (основном) потоке приложения...
Кто вам мешает использовать MessageBox? Этот парень "не отягощен" всякими условностями...
Код:
 MessageBox(0, 'bla bla bla', 'Msg Title', MB_OK + MB_ICONINFORMATION + MB_TOPMOST);
Если нет, то где такое можно делать по завершению работы потока.
см. выше по тексту
2. Если я в потоке вношу данные в Memo, ListView и прочие, то я должен производить синхронизацию с основным потоком?
3. Если я в потоке вношу изменения в глобальные переменные (string, Integer, Boolean и прочие), то я должен производить синхронизацию с основным потоком?
Именно. Причем синхронизацию следует понимать как обеспечение "эксклюзивного" использование общих данных в "точках пересечения" параллельных потоков (они же - нити) :)
 

Shouldercannon

Well-Known Member
25.05.2010
128
0
#5
Код:
 TNewThread = class(TThread)
private
{ Private declarations }
s: string;
Bool: Boolean;
protected
procedure Execute; override;
end;

TNewThread2 = class(TThread)
private
{ Private declarations }
s: string;
Bool: Boolean;
protected
procedure Execute; override;
end;
При таком раскладе переменные string и boolean буду принадлежать тому потоку, в котором объявлены и не буду перепутаны?
P.S. Имена переменных в обоих потоках одинаковые.
 

Shouldercannon

Well-Known Member
25.05.2010
128
0
#7
От теории к практике. Вот, что получилось сделать. Возможно, допущены ошибки.
Код:
 TNewThread = class(TThread)
private
{ Private declarations }
s: string;
Bool: Boolean;
protected
procedure SyncProc;
procedure Execute; override;
procedure DoTerminate; override;
end;

var
Form1: TForm1;
a: string;
DownloadStatus: Boolean;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
NewThread: TNewThread;
begin
Button1.Enabled := False;

NewThread := TNewThread.Create(True); // Поток создан и остановлен
NewThread.FreeOnTerminate := True; // Уничтожить поток после завершения работы
NewThread.Resume; // Запуск потока
end;

procedure TNewThread.Execute;
var
HTTP: TIdHTTP;
MS: TMemoryStream;
begin
Bool := True; // Статус выполнения операции (успешно)

HTTP := TIdHTTP.Create(nil);
MS := TMemoryStream.Create;
try
try
HTTP.Get('http://rvs.ucoz.ru/files/programs/il2sfbc_update.7z', MS);
MS.SaveToFile('il2sfbc_update.7z');
except
on E: Exception do
begin
Bool := False; // Статус выполнения операции (накрылось медным тазом)
s := E.Message;
end; 
end;
finally
HTTP.Free;
MS.Free;
end;

Synchronize(SyncProc); // Синхронизация с основным потоком
end;

procedure TNewThread.SyncProc;
begin
DownloadStatus := Bool; // Синхронизация Boolean
a := s; // Синхронизация string
end;

procedure TNewThread.DoTerminate;
begin
// Что-то... Если нужно до вызова внешнего OnTerminate (если назначен)
inherited;
// Что-то... Если нужно после вызова внешнего OnTerminate (если назначен)
Form1.Button1.Enabled := True;
if DownloadStatus then MessageBox(0, 'Скачивание прошло успешно', 'Информация', MB_ICONInformation) else MessageBox(0, PChar('Произошла ошибка при скачивании. ' + a), 'Ошибка', MB_ICONError) 
end;
Верно?