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


Автор: NewDima 7.5.2009, 16:22
Наследую обычный TThread. В общем не важно, что пишу в execute. Один раз вызываю resume, и поток нормально отрабатывает, никаких поблем. Вызываю после отработки второй раз - в execute не попадаю. Что за дела, кто знает?

Автор: Демо 7.5.2009, 16:25
Цитата(NewDima @  7.5.2009,  16:22 Найти цитируемый пост)
th.Resume;
f := th.WaitFor;


Вот это как понимать?
После Resume th заканчивает свою работу?

Добавлено через 34 секунды
Приведи реализацию TThread.

Автор: NewDima 7.5.2009, 16:29
собственно там ерунду написал, быстро сообразил и отредактировал, потому как к делу отношения не имеет =)
Код

TNewThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure sm;
    procedure Execute; override;
  end;
...
procedure TNewThread.sm;
begin
ShowMessage('dwad');
end;

procedure TNewThread.Execute;
begin
Synchronize(sm);
  { Place thread code here }
end;

Не хитро, правда? В общем по стандарту решил проверить, потому как заметил на своем другом потоке

Автор: Демо 7.5.2009, 16:35
Цитата(NewDima @  7.5.2009,  16:29 Найти цитируемый пост)
Не хитро, правда? В общем по стандарту решил проверить, потому как заметил на своем другом потоке 


Категорически выбрось такие "Стандарты". Видимо, это стандарт Флёнова.

Метод Execute выполняется в отдельном потоке(нити). После того, как этот метод закончился, дополнительный поток терминируется, и ни о каком повторном использовании речи быть не может.

 
Цитата(NewDima @  7.5.2009,  16:29 Найти цитируемый пост)
procedure TNewThread.Execute;
begin
Synchronize(sm);
  { Place thread code here }
end;


Используя таким способом Synchronize, ты выполняешь TNewThread.sm в основном потоке.

Сейчас нарисую пример для повторного использования потока.

Автор: NewDima 7.5.2009, 16:43
Цитата(Демо @ 7.5.2009,  23:35)
Используя таким способом Synchronize, ты выполняешь TNewThread.sm в основном потоке.

не понял, это с каких пор? я перекрываю виртуальный execute и ресумом перевожу поток в состояние готовности для шедуллера винды. Я так полагаю, под словом терминируется вы имели ввиду "освобождается"? ан нет, я не устанавливал FreeOnTerminate  в true

Автор: kami 7.5.2009, 18:15
Цитата(NewDima @  7.5.2009,  16:43 Найти цитируемый пост)
под словом терминируется вы имели ввиду "освобождается"

Не нужно путать виндовые потоки и класс-обертку TThread.
Когда происходит выход из Execute TThread виндовый поток завершается. Винда про него забывает. Все - для нее этого потока больше не существует.
А вот экземпляр класса остается. Но вот сделать он с потоком больше ничего не может. Потому что нет больше этого потока. Цирк уехал, а клоуны остались. В смысле - поток завершен и выброшен на свалку истории, а экземпляр TThread, его методы и свойства остаются доступными для вычисления последствий отработки потока.


Цитата(NewDima @  7.5.2009,  16:43 Найти цитируемый пост)
не понял, это с каких пор?

С самых обычных.
Если в методе Execute есть только  Synchronize, то само наличие дополнительного потока теряет смысл и только усложняет архитектуру приложения.

Добавлено через 6 минут и 7 секунд
Цитата(Демо @  7.5.2009,  16:35 Найти цитируемый пост)
Видимо, это стандарт Флёнова.

+100 !!!

Автор: Демо 7.5.2009, 21:02
Обещанный пример:

Код

unit uRetrThr;

interface

uses
  Windows, Classes;

type

  TRetrThr=class(TThread)
  private
    FSignalJob: THandle;
    FSignalThr: THandle;
    FParam: String;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;

    function Start(const Value: String;
      WaitStarting: Boolean=True): Boolean;
  end;

implementation


{ TRetrThr }

constructor TRetrThr.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FSignalJob := CreateEvent(nil,False,False,'');
  FSignalThr := CreateEvent(nil,False,False,'');
  Resume;
  SetEvent(FSignalThr);
end;

destructor TRetrThr.Destroy;
begin
  CloseHandle(FSignalJob);
  CloseHandle(FSignalThr);
  inherited;
end;

procedure TRetrThr.Execute;
begin
  repeat
    WaitForSingleObject(FSignalJob,Infinite);
    Sleep(3000);
    SetEvent(FSignalThr);
  until Terminated;
end;

function TRetrThr.Start(const Value: String;
  WaitStarting: Boolean=True): Boolean;
var
  RC: Cardinal;
begin
  RC := 1;

  case WaitStarting of
    True: RC := WaitForSingleObject(FSignalThr,INFINITE);
    False: RC := WaitForSingleObject(FSignalThr,0);
  end;

  Result := RC=WAIT_OBJECT_0;
  if not Result then Exit;

  FParam := Value;
  SetEvent(FSignalJob);
end;

end.


Код

unit ufMain;

interface

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

type

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  RH: TRetrThr;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not RH.Start('Âûçîâ',False) then ShowMEssage('Îøèáêà');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  RH := TRetrThr.Create;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  RH.Terminate;
end;

end.


Автор: NewDima 8.5.2009, 03:51
Причем стандарты Фленова? Стандартный делфийский поток создаю, собственно в его каркас вставляю код. А то что в экзэкьюте используется только один метод, вызываемый Synchronize не значит, что я использую его вообще. Настоящий поток устроен совсем по-другому, а этот писал для проверки, дохнет ли виндовская нить при выходе из экзекьюта. Я полагал, что при выходе она впадает в состояние приостановки, в этом и ошибся.
А по архитектуре приложения, то мне нужен класс, которому устанавливаешь задание и запускаешь, причем во время работы его можно было корректно приостановить (не саспендом в моем случае), а потом продолжить работу далее. А по выполнению задания он должен останавливаться. Это я реализовал, только проще, и только уже утром, когда проспался. Всем удачи
PS кстати, о Фленове, даже не подозреваю, кто это, если пишет книги, то я ни одной книги не прочитал, только мсдн

Автор: Демо 8.5.2009, 10:03
Цитата(NewDima @  8.5.2009,  03:51 Найти цитируемый пост)
Это я реализовал, только проще, и только уже утром, когда проспался.


Ну так поделился бы своим решением.

Автор: NewDima 8.5.2009, 10:56
Код

type
  TDownloadThread = class (TThread)
    private
      FForPause: boolean;
      FConnection: HINTERNET;
      FConnected: boolean;
      FFileName: string;
      FError: integer;

      FSheduled: boolean;
      FSheduleURL: string;
      FSheduleStart: integer;
      FSheduleLen: integer;
      FSheduleError: integer;

      FOnDownloaded: TNotifyEvent;
    protected
      procedure Execute(); override;
      procedure Connect();
      procedure DoLoaded();
      function  ReadPeace(): integer;
    public
      property    Connected: boolean read FConnected;
      property    ErrorCode: integer read FError;
      property    OnDownloaded: TNotifyEvent read FOnDownloaded write FOnDownloaded;
      constructor Create(AFileName: string);
      destructor  Destroy();
      function    Download(Url: string; Start, Len: integer): integer;
      function    DownloadAgain(): integer;
      procedure   PauseDownloading();
    published
  end;

implementation

constructor TDownloadThread.Create(AFileName: string);
begin
inherited Create(true);
...
Connect();
end;

destructor TDownloadThread.Destroy;
begin
...
inherited;
end;

procedure TDownloadThread.DoLoaded();
begin
FOnDownloaded(self);
End;

procedure TDownloadThread.Connect();
begin
...
end;

procedure TDownloadThread.Execute;
begin
repeat
ReadPeace();
if (FSheduleLen <= 0) or (not FForPause) then
  FSheduled := false;
FError := FSheduleError;
if (FSheduleLen<=0) and (Assigned(FOnDownloaded)) then
  Synchronize(DoLoaded);
Suspend;
until Terminated;
end;

function TDownloadThread.Download(Url: string; Start, Len: integer): integer;
begin
if FSheduled then exit;
FSheduled := true;
FSheduleStart := Start;
FSheduleLen := Len;
FSheduleURL := Url;
Resume;
end;

function TDownloadThread.DownloadAgain(): integer;
begin
if not FSheduled then exit;
FForPause := false;
Resume();
end;

procedure TDownloadThread.PauseDownloading;
begin
FForPause := true;
end;

function TDownloadThread.ReadPeace(): integer;
begin
...
end;
end.


Автор: Демо 8.5.2009, 18:41
NewDima, 
Не хочу сказать ничего плохого, но алгоритм крайне ненадёжен.
Возможны одновременные успешные попытки выполнения TDownloadThread.Download.

Автор: MetalFan 8.5.2009, 22:08
вообще не очень хорошо использовать suspend-resume...

Автор: NewDima 9.5.2009, 05:16
MetalFan, хотелось бы увидеть аргументы, хотя я и сам так подумываю, но все-таки =)

Автор: Alexeis 9.5.2009, 12:06
Цитата(NewDima @  9.5.2009,  04:16 Найти цитируемый пост)
хотелось бы увидеть аргументы

  suspend-resume заморозит поток в произвольном состоянии, это все равно что не останавливать. Получиться незавершенная операция и все вытекающие проблемы синхронизации и одновременного доступа к общей памяти.

Автор: NewDima 9.5.2009, 14:41
дело в том, что suspend и resume вызываются только в самом классе, причем в соответствующих случаях закрываются(или открываются) нужные дескрипторы. Разве это не должный контроль ресурсов?

Добавлено через 14 минут и 17 секунд
Демо, ты имеешь ввиду, что два других потока попытаются выполнить этот метод? Если честно, не совсем понимаю, почему такое возможно

Автор: Alexeis 9.5.2009, 15:41
Цитата(NewDima @  9.5.2009,  13:41 Найти цитируемый пост)
дело в том, что suspend и resume вызываются только в самом классе, причем в соответствующих случаях закрываются(или открываются) нужные дескрипторы. Разве это не должный контроль ресурсов?

   Как можно определить тот самый момент? Кроме того ничего не мешает вызывать методы наследника TThread из чужого потока и получить остановку в неопределенный момент. Определить нужный момент можно заставив поток ждать нужного момента, например на WaitForSingleObject/GetMessage/SendMessage и т.д. Раз уж мы смогли остановить поток в нужный момент, то зачем делать дополнительный suspend, ведь он и так спит ?

Автор: Демо 9.5.2009, 16:37
Цитата(NewDima @  9.5.2009,  14:41 Найти цитируемый пост)
Демо, ты имеешь ввиду, что два других потока попытаются выполнить этот метод? Если честно, не совсем понимаю, почему такое возможно 


Цитата(NewDima @  8.5.2009,  10:56 Найти цитируемый пост)
function TDownloadThread.Download(Url: string; Start, Len: integer): integer;
begin
//                                     Здесь входит первый поток
if FSheduled then exit;
//                                     Здесь входит второй поток
//                                     ...
//                                     Здесь входит N-ый поток
//                                     Все они выполняют код ниже.

FSheduled := true;
FSheduleStart := Start;
FSheduleLen := Len;
FSheduleURL := Url;
Resume;
end;


Автор: NewDima 9.5.2009, 16:54
Спс, я подумаю

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