Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 14.12.2011, 16:45   #1
Shouldercannon
Участник клуба Подтвердите свой е-майл
 
Аватар для Shouldercannon
 
Регистрация: 26.01.2008
Сообщений: 1,898
Вопрос Вопросы по потокам TThread

1. Можно ли в потоке считывать данные из переменной, которая объявлена глобальной (string, integer, boolean)?
Код:
var
  Form1: TForm1;
  s: string;
2. Можно ли в потоке изменять содержимое глобальной переменной (string, integer, boolean)?
Код:
var
  Form1: TForm1;
  s: string;
Нужна ли при этом сихронизация?
3. В каких случаях нужно обращаться к переменным или другим объёктам в потоке через Form1.Label1.Caption := ''; и тд.?
4. Каким образом по завершению работы потока вывести сообщение? Знаю, что за это отвечает событие Terminate.
5. Как будет правильно выглядеть скелет потока с процедурой Terminate?
Явно не так
Код:
procedure TForm1.sBDownloadClick(Sender: TObject);
var d:TDownLoader; 
begin
//Создадим класс потока.
//Поток для начала будет остановлен
d:=TDownLoader.Create(true);
//Передадим параметры потоку
d.URL:=Edit1.Text;
d.ToFolder:=ExtractFilePath(ParamStr(0))+Edit2.Text;
//Поток должен удалить себя по завершению своей работы
d.FreeOnTerminate:=true;
//И запустим его на закачку.
d.Resume;
//Теперь с процедуры мы выйдем, но поток работает
//и живёт своей жизней
//Поток должен удалить себя по завершению своей работы
d.FreeOnTerminate:=true;
d.OnTerminate:=thrTerminate;
end;
6. Как в переменные потока записать данные из глобальных переменны?
Пока вроде всё.
Shouldercannon вне форума Ответить с цитированием
Старый 14.12.2011, 18:27   #2
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Можно ли в потоке считывать данные из переменной, которая объявлена глобальной (string, integer, boolean)?
Можно. Почему нет?

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Можно ли в потоке изменять содержимое глобальной переменной (string, integer, boolean)?
Можно.

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Нужна ли при этом сихронизация?
Обязательно. MREW, критическая секция или хотя бы Synchronize.

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
В каких случаях нужно обращаться к переменным или другим объёктам в потоке через Form1.Label1.Caption := ''; и тд.?
Ни в каких. VCL однопоточна, из другого потока обращаться к визуальным компонентам - нельзя. Сохраните данные, которые вам нужны, из компонент в переменные. Пусть поток читает переменные.

И наоборот. Надо передать - пусть поток сохранит в переменные. А форма перенесёт данные из переменных в компоненты.

Делается это через Synchronize или оконные сообщения.

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Каким образом по завершению работы потока вывести сообщение? Знаю, что за это отвечает событие Terminate.
Вот в нём и выводите. Это как вариант.

Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Как будет правильно выглядеть скелет потока с процедурой Terminate?
Не понял, в чём проблема.

Например, так (заодно показан пример передачи значения переменной потоку):

Код:
type
  TMyThread = class(TThread)
  private
    FValue: Integer;
    procedure TerminateHandler(Sender: TObject);
  protected
    procedure Execute; override;
    property Value: Integer read FValue;
  public
    constructor Create(const AValue: Integer);
  end;

{ TMyThread }

constructor TMyThread.Create(const AValue: Integer);
begin
  FValue := AValue;
  OnTerminate := TerminateHandler;
  inherited Create(False);
end;

procedure TMyThread.Execute;
begin
  // код, использующий Value
end;

procedure TMyThread.TerminateHandler(Sender: TObject);
begin
  // код выхода
end;
Цитата:
Сообщение от Shouldercannon Посмотреть сообщение
Как в переменные потока записать данные из глобальных переменны?
Возможно два случая: 1. вам нужно передать потоку значение глобальной переменной при запуске. 2. вам нужно в потоке время от времени читать глобальную переменную (использовать актуальное значение).

В случае 1 вам нужно передать значение переменной в конструктор потока или же в поле (до запуска потока). Во втором случае - нужно синхронизировать доступ (MREW/CS/Synchronize).
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 14.12.2011, 18:45   #3
Shouldercannon
Участник клуба Подтвердите свой е-майл
 
Аватар для Shouldercannon
 
Регистрация: 26.01.2008
Сообщений: 1,898
По умолчанию

Данная запись будет считаться верной?
Код:
procedure TDownload_ThreadTerminate(Sender: TObject);
...
  TDownload_Thread = class(TThread)
  private
    { Private declarations }
    SyncMsg: string;
    procedure DoSyncProc;
    procedure HTTPWork(Sender: TObject; AWorkMode: TWorkMode;
      const AWorkCount: Integer);
    procedure HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode;
      const AWorkCountMax: Integer);
  protected
    procedure Execute; override;
  public
  end;
...
procedure TFormMain.Download;
var
  Download_Thread: TDownload_Thread;
begin
  // Создадим класс потока. Поток для начала будет остановлен
  Download_Thread := TDownload_Thread.Create(True);
  // Поток должен удалить себя по завершению своей работы
  Download_Thread.FreeOnTerminate := True;
  // И запустим его на закачку
  Download_Thread.Resume;

  // Теперь с процедуры мы выйдем, но поток работает и живёт своей жизней. Поток должен удалить себя по завершению своей работы
  Download_Thread.FreeOnTerminate := True; // Зачем два раза писать одно и тоже?
  Download_Thread.OnTerminate := TDownload_ThreadTerminate;
end;
...
procedure TDownload_Thread.Execute;
var
  HTTP: TIdHTTP;
  TMS: TMemoryStream;
begin
  ErrorMessage := ''; // Изменение глобальной переменной типа string. Нужна сихронизация?

  HTTP := TIdHTTP.Create(nil);
  TMS := TMemoryStream.Create;
  try
    HTTP.OnWork := HTTPWork;
    HTTP.OnWorkBegin := HTTPWorkBegin;
    try
      HTTP.Get('http://77.108.194.247/' + FileName, TMS);
      TMS.SaveToFile(Path + '\' + Copy(FileName, 1, Pos('.', FileName) - 1) + '\' + FileName); // Тут берутся данные из глобальных переменных
    except
      on E: Exception do
      begin
        SyncMsg := E.Message;
        ErrorDownloading := True; // Изменение глобальной переменной типа Boolean. Нужна сихронизация?
        Synchronize(DoSyncProc);
      end;
    end;
  finally
    HTTP.Free;
    TMS.Free;
  end;
end;
...
procedure TDownload_Thread.DoSyncProc;
begin
  ErrorMessage := SyncMsg;
end;
...
procedure TFormMain.TDownload_ThreadTerminate(Sender: TObject);
begin
//
end;
Shouldercannon вне форума Ответить с цитированием
Старый 14.12.2011, 18:52   #4
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Ээээ... я даже не знаю, что сказать.

Цитата:
// Зачем два раза писать одно и тоже?
Это вы сами с собой разговариваете? О_о
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 14.12.2011, 18:58   #5
Shouldercannon
Участник клуба Подтвердите свой е-майл
 
Аватар для Shouldercannon
 
Регистрация: 26.01.2008
Сообщений: 1,898
По умолчанию

Цитата:
Сообщение от GunSmoker Посмотреть сообщение
Ээээ... я даже не знаю, что сказать.



Это вы сами с собой разговариваете? О_о
Скажите хоть что-нибудь
Это комментарий, хотя получилось забавно . Я этот код нашёл в сети. И всётаки зачем два раза одно и тоже написано и вообще допустимо такое построеие потока?

Последний раз редактировалось Shouldercannon; 14.12.2011 в 19:00.
Shouldercannon вне форума Ответить с цитированием
Старый 14.12.2011, 19:01   #6
haruhi
Форумчанин
 
Аватар для haruhi
 
Регистрация: 05.10.2011
Сообщений: 368
По умолчанию

из потока можно без каких-либо опасений можно взаимодействовать с VCL через оконные сообщения (SendMessage)

Код:
Download_Thread.OnTerminate := TDownload_ThreadTerminate;
было бы правильнее если TDownload_ThreadTerminate была функцией-членом класса форма
Не стоит будить спящего Бога! (с) Меланхолия Харухи Судзумии

Последний раз редактировалось haruhi; 14.12.2011 в 19:05.
haruhi вне форума Ответить с цитированием
Старый 14.12.2011, 19:36   #7
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Есть предложение выкинуть такой код и написать самому.

Явная ошибка - TDownload_ThreadTerminate должен быть методом, а не процедурой (в чём разница?).

Кроме того, код в целом достаточно коряво написан. Я бы сделал так:
1. Инициализация потока - только в его конструкторе.
2. Все значения, что необходимы потоку для работы - передавать в конструктор.
3. Обработка ошибок - стандартная от TThread.
4. Окончание работы - либо синхронно, либо асинхронно с сообщением.
5. Вынес бы код потока в отдельный модуль.

Итого:
Код:
unit DownloadThread;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;

const
  WM_ThreadDone = WM_USER + 1;

type
  TWMThreadDone = record
    Msg: Cardinal;
    Success: Boolean;
    ErrorMsg: PChar;
    Result: LRESULT;
  end;

  TDownloadThread = class(TThread)
  private
    FWnd: HWND;
    FPath: String;
    FFileName: String;
    procedure HTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer);
    procedure HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer);
    procedure Done(Sender: TObject);
  protected
    procedure Execute; override;
    property Wnd: HWND read FWnd;
    property Path: String read FPath;
    property FileName: String read FFileName;
  public
    constructor Create(const APath, AFileName: String; const AReportErrorWnd: HWND = 0);
    property Terminated;
  end;

implementation

constructor TDownloadThread.Create(const APath, AFileName: String;
  const AReportErrorWnd: HWND);
begin
  // Настройка потока:
  FWnd := AReportErrorWnd;
  FreeOnTerminate := (AReportErrorWnd <> 0);
  FPath := APath;
  FFileName := AFileName;
  OnTerminate := Done;

  // Поехали! (С)
  inherited Create(False);
end;

procedure TDownloadThread.Execute;
var
  HTTP: TIdHTTP;
  FS: TFileStream;
begin
  HTTP := TIdHTTP.Create(nil);
  try
    HTTP.OnWork := HTTPWork;
    HTTP.OnWorkBegin := HTTPWorkBegin;

    FS := TFileStream.Create(IncludeTrailingPathDelimiter(Path) + Copy(FileName, 1, Pos('.', FileName) - 1) + PathDelim + FileName, fmCreate or fmShareExclusive);
    try
      HTTP.Get('http://77.108.194.247/' + FileName, FS);
    finally
      FreeAndNil(FS);
    end;
  finally
    FreeAndNil(HTTP);
  end;
end;

procedure TDownloadThread.Done(Sender: TObject);
var
  Success: Bool;
  Msg: String;
begin
  // установить Terminated (для синхронного вызова)
  Terminate;

  // уведомить окно (для асинхронного вызова)
  if Wnd <> 0 then
  begin
    Success := not Assigned(FatalException);
    if Success then
      Msg := ''
    else
      Msg := Exception(FatalException).Message;

    SendMessage(Wnd, WM_ThreadDone, Integer(Success), Integer(PChar(Msg)));
  end;
end;

procedure TDownloadThread.HTTPWork(Sender: TObject; AWorkMode: TWorkMode;
  const AWorkCount: Integer);
begin
  //
end;

procedure TDownloadThread.HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode;
  const AWorkCountMax: Integer);
begin
  //
end;

end.
Код:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    procedure ThreadDone(var Msg: TWMThreadDone); message WM_ThreadDone;

    // Показано два варианта использования потока
    procedure Download; overload; 
    procedure Download(const APath, AFile: String); overload;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// Показано два варианта использования потока

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

procedure TForm1.Button2Click(Sender: TObject);
begin
  Download('C:\MyFiles', 'filename1.txt');
end;

// Асинхронный запуск:
procedure TForm1.Download;
begin
  TDownloadThread.Create('C:\MyFiles', 'filename1.txt', Handle); // <- Handle указывает окно для сброса ему уведомления
end;

procedure TForm1.ThreadDone(var Msg: TWMThreadDone); // <- само уведомление
begin
  if Msg.Success then
    Caption := 'Уря!'
  else
    Caption := Msg.ErrorMsg;
end;

// Синхронный запуск:
procedure TForm1.Download(const APath, AFile: String);
var
  Thread: TDownloadThread;
begin
  Thread := TDownloadThread.Create(APath, AFile);
  try
    // Ждём окончания работы потока
    while not Thread.Terminated do
      Application.HandleMessage;

    if Assigned(Thread.FatalException) then
      // Ошибка:
      Caption := Exception(Thread.FatalException).Message
    else
      Caption := 'Уря!';
  finally
    FreeAndNil(Thread);
  end;
end;

end.
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 19.12.2011, 15:30   #8
Shouldercannon
Участник клуба Подтвердите свой е-майл
 
Аватар для Shouldercannon
 
Регистрация: 26.01.2008
Сообщений: 1,898
По умолчанию

Возможно, я где-то не внимательно посмотрел, но где по окончанию работы потока можно, например, запустить другое приложение?
Shouldercannon вне форума Ответить с цитированием
Старый 19.12.2011, 15:35   #9
GunSmoker
Старожил
 
Регистрация: 13.08.2009
Сообщений: 2,581
По умолчанию

Можно .
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
GunSmoker вне форума Ответить с цитированием
Старый 19.12.2011, 15:46   #10
Shouldercannon
Участник клуба Подтвердите свой е-майл
 
Аватар для Shouldercannon
 
Регистрация: 26.01.2008
Сообщений: 1,898
По умолчанию

Скорее всего это надо делать здесь
Код:
procedure ThreadDone(var Msg: TWMThreadDone); message WM_ThreadDone;
Shouldercannon вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Вопросец по потокам stlcrash Общие вопросы Delphi 8 11.07.2016 19:36
При освобождении обьекта TThread, он вешает всю программу. При этом код Execute у TThread уж выполнился.. Человек_Борща Общие вопросы Delphi 23 30.11.2011 09:18
Вопрос по потокам Karamzda Общие вопросы C/C++ 1 17.11.2010 16:43
Вопрос по потокам mspavlyxa Помощь студентам 6 25.03.2010 23:46
Вопрос по потокам Eretic Общие вопросы по Java, Java SE, Kotlin 5 02.04.2009 00:17