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

Тема в разделе "Delphi - Компоненты", создана пользователем Shouldercannon, 15 дек 2011.

  1. Shouldercannon

    Shouldercannon Well-Known Member

    Регистрация:
    25 май 2010
    Сообщения:
    125
    Симпатии:
    0
    Данный поток построен верно?
    Код (Text):
    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), а не отдельно, если это вообще возможно?
     
  2. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Почти... :rolleyes:
    Код (Delphi):
    // Вот так. Сначала устанавливаем все свойства, а потом уже запускаем
    procedure TForm1.Button1Click(Sender: TObject);
    var
    NewThread: TNewThread;
    begin
    NewThread := TNewThread.Create(True); // Поток создан и остановлен
    NewThread.OnTerminate := TNewThread_ThreadTerminate;
    NewThread.FreeOnTerminate := True; // Уничтожить поток после завершения работы
    NewThread.Resume; // Запуск потока
    end;
    Если я правильно понял Ваш вопрос, то вот так
    Код (Delphi):
    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;
     
  3. Shouldercannon

    Shouldercannon Well-Known Member

    Регистрация:
    25 май 2010
    Сообщения:
    125
    Симпатии:
    0
    1. В procedure TNewThread.DoTerminate; можно делать что угодно: обращаться к компонентам формы, вносить изменения в глобальные переменные, запускать другие приложения, закрывать формы и т.д.? ShowMessage там точно нельзя делать, окна получаются корявыми (либо огромные, либо широкие). Если нет, то где такое можно делать по завершению работы потока.
    2. Если я в потоке вношу данные в Memo, ListView и прочие, то я должен производить синхронизацию с основным потоком?
    3. Если я в потоке вношу изменения в глобальные переменные (string, Integer, Boolean и прочие), то я должен производить синхронизацию с основным потоком?
     
  4. sinkopa

    sinkopa Well-Known Member

    Регистрация:
    17 июн 2009
    Сообщения:
    344
    Симпатии:
    9
    Ну... лекцию я тут Вам читать не буду... курите Google.
    Ключевые слова: Параллельные потоки, Критические секции (TCriticalSection) , Событие (TEvent), Мьютекс (mutex), семафор (semaphore)
    Если коротко (и слегка утрируя) Всё можно... если осторожно... :newconfus:
    Несколько коротких примеров
    1. Через Synchronize
    Код (Delphi):
    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. Через критическую секцию
    Код (Delphi):
    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
    Код (Delphi):
    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
    Код (Delphi):
    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... выяснится что это на самом деле Диалог...
    А диалоги вообще-то ориентированы на работе в главном (основном) потоке приложения...
    Кто вам мешает использовать MessageBox? Этот парень "не отягощен" всякими условностями...
    Код (Delphi):
     MessageBox(0, 'bla bla bla', 'Msg Title', MB_OK + MB_ICONINFORMATION + MB_TOPMOST);
    см. выше по тексту
    Именно. Причем синхронизацию следует понимать как обеспечение "эксклюзивного" использование общих данных в "точках пересечения" параллельных потоков (они же - нити) :)
     
  5. Shouldercannon

    Shouldercannon Well-Known Member

    Регистрация:
    25 май 2010
    Сообщения:
    125
    Симпатии:
    0
    Код (Delphi):
     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. Имена переменных в обоих потоках одинаковые.
     
  6. Vadik(R)

    Vadik(R) Well-Known Member

    Регистрация:
    12 дек 2007
    Сообщения:
    483
    Симпатии:
    0
    Нет, перепутаны не будут.
     
  7. Shouldercannon

    Shouldercannon Well-Known Member

    Регистрация:
    25 май 2010
    Сообщения:
    125
    Симпатии:
    0
    От теории к практике. Вот, что получилось сделать. Возможно, допущены ошибки.
    Код (Delphi):
     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;
    Верно?
     
Загрузка...

Поделиться этой страницей