Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.06.14;
Скачать: [xml.tar.bz2];

Вниз

Использование inherited   Найти похожие ветки 

 
Digitman ©   (2005-05-30 16:32) [40]


> Mouserx   (30.05.05 16:27) [37]
> Просто Thread.Destroy?
> Не противоречит ли с [21]?


да, просто Thread.Free

нет, не противоречит, если FreeOnTerminate=False


> Т.е в начале


не обязательно.

но ДО того как ты уничтожишь ресурсы, к которым доп.поток обращается в ходе работы


 
Mouserx   (2005-05-30 16:37) [41]

Digitman ©   (30.05.05 16:32) [40]

Из вне так будет хорошо работать, но как доп. поток освободит ресурсы сам, если FreeOnTerminate=False. Например когда он закончит с расчетами.


 
evvcom ©   (2005-05-30 16:40) [42]


> как доп. поток освободит ресурсы сам

Освобождай в Destroy. Как вызовешь Free, так и освободит.


 
Mouserx   (2005-05-30 16:42) [43]


> Как вызовешь Free, так и освободит.

Т.е в конце метода Execute необходимо написать Self.Free?


 
Digitman ©   (2005-05-30 16:48) [44]


> Mouserx   (30.05.05 16:42) [43]
> Т.е в конце метода Execute необходимо написать Self.Free?


да какой еще Self.Free ?

метод Free поточного объекта ты вызовешь в деструкторе своего контролирующего потоки объекта, при этом сначала в методе Terminate будет установлен флаг Terminated (его ты можешь периодически контролировать в теле Execute, и как только увидишь что он True, как можно быстрее завершай работу метода), затем будет вызван метод WaitFor (он дождется завершения работы потока как ОС-объекта) и следом же будет вызван метод Free, который приведет к разрушению потока уже как VCL-объекта


 
Digitman ©   (2005-05-30 16:50) [45]


> следом же будет вызван метод Free


тьфу ты !

не Free, а inherited Destroy предка


 
evvcom ©   (2005-05-30 16:51) [46]

1. Если ты хочешь грохнуть поток сразу по завершении, то проще использовать FreeOnTerminate := True;
2. Если ты собираешься использовать поток повторно, то грохать его не надо сразу. Поэтому вызов Thread.Free должен быть где-то из основного потока. А в Execute даже не выходя из цикла Suspend;
3. В любом случае доп.поток должен как-то сообщить основному, что работу он выполнил. В случае FreeOnTerminate := False; здесь из основного потока и можно вызвать Thread.Free.


 
Mouserx   (2005-05-30 16:51) [47]


> Digitman ©   (30.05.05 16:48) [44]

Я имел ввиду не снаружи а освобождение ресурсов, когда поток сам, без стороних "указов", завершит работу. Смотри [41]


 
Digitman ©   (2005-05-30 16:54) [48]

destructor TMyThread.Destroy;
begin
 inherited; //здесь будут вызваны Terminate, WaitFor и inherited

//здесь поток как ОС-объект уже завершил свою работу
//никто более не обращается ни к fFindedList ни к fIgnoreList
//поэтому именно здесь их можно смело уничтожить

 fFindedList.Free; {TStringList}
 fIgnoreList.Free; {TStringList}
end;


 
Mouserx   (2005-05-30 16:55) [49]

Ок. Всем большое Спасибо.
Буду щас переваривать и делать как вы мне обьяснили :-)


 
Digitman ©   (2005-05-30 16:59) [50]


> Mouserx   (30.05.05 16:51) [47]


ну и что ?

ну, предположим, завершился он раньше чем твой класс-контролер решил его уничтожить ..

при этом работает та же схема - [48]

разница будет лишь в одном - WaitFor в дан.случае немедленно вернет управление (потому что поточная ф-ция уже завершилась и ждать уже некого), в то время как вызов WaitFor для еще работающей поточной ф-ции приведет к блокированию вызывающего потока на время до завершения целевой поточной ф-ции


 
Defunct ©   (2005-05-30 17:05) [51]

> Mouserx
Обещанный пример, поковыряйте

unit uThreadExtDestoryExample;

interface

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

const
 WM_FINALIZINGTHREADOBJ = WM_USER + 1;

type
 TForm1 = class(TForm)
   Memo1: TMemo;
   Button1: TButton;
   Button2: TButton;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
 private
   procedure AcceptThreadData( var MSG: TMessage);message WM_FINALIZINGTHREADOBJ;
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

type
  TMyThread = class(TThread)
  private
    fList : TStrings;
    fOwnerHandle : HWND;
    fCanDestroy  : boolean;
  protected
    procedure execute;override;
  public
    constructor Create(AOwner : HWND);
    destructor  Destroy;override;
  end;

constructor TMyThread.Create;
begin
  fOwnerHandle := AOwner;
  inherited Create(True);
  fList := TStringList.Create;
  fCanDestroy := false;
  Resume;
end;

procedure TMyThread.execute;
var
 i : integer;
begin
 i := 0;
 while not Terminated and (i < 100) do
 // Должен завершиться через 10 сек, если нет аварийного завершения
   begin

      fList.Add(
        format("Поток=%0.8X, Нормальная работа, рандом(X)=%0.8X",
               [GetCurrentThreadId, Random( High(Integer) )] )
               );
      Sleep(100);
      inc(i);
   end;

 if Terminated then
     fList.Add(
        format("Поток=%0.8X, АВАРИЙНОЕ ЗАВЕРШЕНИЕ",
               [GetCurrentThreadId, Random( High(Integer) )] )
               );

 fCanDestroy := True;
end;

destructor TMyThread.Destroy;
begin
// На случай если Free вызван из-вне
 Terminate;
 while not fCanDestroy do Sleep(100);

 SendMessage( fOwnerHandle, WM_FINALIZINGTHREADOBJ, Integer(fList), 0);
 fList.Free;
 inherited;
end;


var
 Thread : TMyThread = nil; // Для теста аварийного завершения

procedure TForm1.Button1Click(Sender: TObject);
begin
// Запуск потока
 Thread := TMyThread.Create( Handle )
end;

procedure TForm1.AcceptThreadData;
var
 List : TStrings;
begin
 List := TStrings( Msg.WParam );
 Memo1.Lines.Text := List.Text
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
// Аварийное завершение потока
 if Assigned(Thread) then
    FreeAndNil( Thread );
end;

end.


 
Mouserx   (2005-05-30 17:12) [52]


> Defunct ©   (30.05.05 17:05) [51]

Спасибо.


 
Defunct ©   (2005-05-30 17:20) [53]

Так с авторазрушением

unit uThreadExtDestoryExample;

interface

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

const
 WM_FINALIZINGTHREADOBJ = WM_USER + 1;

type
 TForm1 = class(TForm)
   Memo1: TMemo;
   Button1: TButton;
   Button2: TButton;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
 private
   procedure AcceptThreadData( var MSG: TMessage);message WM_FINALIZINGTHREADOBJ;
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

type
  TMyThread = class(TThread)
  private
    fList : TStrings;
    fOwnerHandle : HWND;
    fCanDestroy  : boolean;
    fDestructorPending : Boolean;
  protected
    procedure execute;override;
  public
    constructor Create(AOwner : HWND);
    destructor  Destroy;override;
  end;

constructor TMyThread.Create;
begin
  fOwnerHandle := AOwner;
  inherited Create(True);
  fList := TStringList.Create;
  FreeOnTerminate := True;
  fCanDestroy := false;
  fDestructorPending := false;
  Resume;
end;

procedure TMyThread.execute;
var
 i : integer;
begin
 i := 0;
 while not Terminated and (i < 100) and not fDestructorPending do
 // Должен завершиться через 10 сек, если нет аварийного завершения
   begin

      fList.Add(
        format("Поток=%0.8X, Нормальная работа, рандом(X)=%0.8X",
               [GetCurrentThreadId, Random( High(Integer) )] )
               );
      Sleep(100);
      inc(i);
   end;

 if fDestructorPending then
    begin
      fList.Add(
         format("Поток=%0.8X, АВАРИЙНОЕ ЗАВЕРШЕНИЕ",
               [GetCurrentThreadId, Random( High(Integer) )] )
               );
      FreeOnTerminate := false
    end;

 fCanDestroy := True;
end;

destructor TMyThread.Destroy;
begin
// На случай если Free вызван из-вне
 fDestructorPending := True;
 while not fCanDestroy do Sleep(100);

 SendMessage( fOwnerHandle, WM_FINALIZINGTHREADOBJ, Integer(fList), 0);
 fList.Free;
 inherited;
end;

var
 Thread : TMyThread = nil; // Для теста аварийного завершения

procedure TForm1.Button1Click(Sender: TObject);
begin
// Запуск потока
 Thread := TMyThread.Create( Handle )
end;

procedure TForm1.AcceptThreadData;
var
 List : TStrings;
begin
 List := TStrings( Msg.WParam );
 Memo1.Lines.Text := List.Text
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
// Аварийное завершение потока
 if Assigned(Thread) then
    FreeAndNil( Thread );
end;

end.


 
jack128 ©   (2005-05-30 17:54) [54]

Defunct ©   (30.05.05 16:15) [33]
Но, не находите, это возможно уже ошибка в проектировании, что может привести к серьезным глюкам.

Возможно ошибка в проэктировании.. А возможно и нет..  
Это просто первый вариант, который пришел мне в голову..


 
Mouserx   (2005-05-30 18:13) [55]

Defunct ©   (30.05.05 17:20) [53]
Пример очень понятен и похож на мой случай.

Но вот что произойдет при одновременном срабатывании Sinchronize со стороны доп. потока и Button2Click со стороны основного потока?
Получится что оба потока будут вечно ждать друг друга?


 
Mouserx   (2005-05-30 18:18) [56]

Поправка к [55]:

Sinchronize в твоем примере нету, но в моем случае имеется. я конечно перед его использованием раньше проверял на if Terminated.

Но Sinchronize может уже успеть выставится ... т.е поток уснет и будет ждать, пока он сможет выполнить код в другом потоке.
При этом в этот же момент вызывается из вне метод Free потока.


 
Defunct ©   (2005-05-30 19:48) [57]

Mouserx   (30.05.05 18:18) [56]

Возможная проблема: после входа в Destructor выполнение метода Synchronize.

Однако эта проблема решается весьма просто: перед вызовом Synchronize проверить переменную fDestructorPending, и не заходить не выполнять Synchronize если DestructorPending.

Ситуация о которой вы говорите невозможна (если не используется Application.ProcessMessages внутри синхронизируемого метода).

синхронизируемый метод и событие Button2Click выполняются последовательно в одном потоке, и если насильно (ProcessMessages) либо как-то еще выполнение синхронизируемого метода не прерывается, то попасть в обработчик Button2Click можно только после завершения работы синхронизируемого метода.


 
Defunct ©   (2005-05-30 19:54) [58]

Defunct ©   (30.05.05 19:48) [57]

Хотя, возможно придется добавить еще один флаг. Synchronize в вашем случае этот как палка в колесо ;>


 
Gek1   (2005-05-30 20:09) [59]


> Однако эта проблема решается весьма просто: перед вызовом
> Synchronize проверить переменную fDestructorPending, и не
> заходить не выполнять Synchronize если DestructorPending


Не согласен.

Например ситуация:
Доп. поток (даже проверив перед Synchronize переменную fDestructorPending) "входит" в ожидание синхронизации.
В этот момент основной поток "занят" (гуляет по коду). Гуляя по коду вдруг, например, резко понадобилось аварийно остановить программу. Ясное дело потоки мы тоже пытаемся остановить. Вызывая метод Free дополнительного потока мы натыкаемся на след. конструкцию:
while not fCanDestroy do Sleep(100);

Ну и в результате основной поток ждет пока fCanDestroy не будет true, а доп. поток ничего не может делать. Он сам ждет выполнения кода основного потока через Synchronize.


 
Gek1   (2005-05-30 20:11) [60]

Mouserx и Gek1 в данный момент одно и тоже.
(Написал с другого компьютера и не заметил ник)


 
Defunct ©   (2005-05-30 20:27) [61]

Gek1   (30.05.05 20:09) [59]

Это уже другая проблема. И вы прекрасно поняли как все работает, раз углядели такую ситуацию.

Возможно даже, в таком случае (когда точно известно, что поток в настоящее время ожидает возможности выполнить (но еще не приступил к выполнению) метод в основном потоке), насильное разрушение потока в деструкторе TermninateThread будет безболезнено.

PS: Мне очень мешает дать вам правильный совет мое неведенье вашей задачи (вот например, не понимаю зачем в потоке используется Synchronize).


 
Mouserx   (2005-05-30 20:51) [62]


> Мне очень мешает дать вам правильный совет мое неведенье
> вашей задачи (вот например, не понимаю зачем в потоке используется
> Synchronize).

Через метод Synchronize из доп. потока вызываються public св-ва класса TCharacter.
Думаю вот этот линк даст вам понять чем мы занимаемся:
http://delphimaster.net/view/6-1116426647/

Так вот в доп. потоках "крутится" скриптовый движок Pascal Script и к нему дополнения под данную задачу. Ясное дело я не могу аварийно завершить сам движок, и (в лучшем случае) потерять обьекты, созданные в доп. потоке при выполнении определенной комманды из самого скрипта вместе с самим движком.

В данном случае поток может завершиться сам (например выполниться полностью скрипт) или же (при разных ситуациях) мне надо в деструкторе Destroy класса TCharacter "попросить" потоки завершиться при первой возможности. При этом желательно дождаться пока эти потоки действительно завершаться. Также я еще не продумал как обойти тупиковую ситуацию с Synchronize [59]


 
Defunct ©   (2005-05-30 21:14) [63]

Значится так, потоков много и каждый из них может выполняться в основном.

В таком случае, предже чем выполнять аварийное завершение потоков надо быть полностью уверенными в том, что ни один из потоков в настоящее время не выполняется и не ожидает выполнения в главном потоке.

Пишем управляюший класс монитор главного потока, нечто похожее на TCriticalSection из SyncObj, за одним исключением: при нормальной работе при вызове метода Enter увеличивает на 1 внутренний счетчик входов, при вызове Leave уменьшается на 1 внунтренний счетчик. Когда требуется выполнить аварийное завершение потоков, в мониторе главноего потока устанавливаете флаг запрещающий доступ к главному потоку (это будет сопровождаться например так: функция Enter будет возвращать False, а вы в свою очередь в потоках получив значение false не будете запускать метод Synchronize) и периодически проверяете количество входов сопровождая каждую проверку вызовом ProcessMessages. Когда это количество входов равно 0, это значит, ни один из потоков более не ждет обращения к основному потоку, и вы смело можете любой из потоков аварийно завершить как примере [53]. Завершив аварийно какие-то потоки, можете продолжить нормальную работу, сняв флаг запрещающий доступ к главному потоку.


 
Defunct ©   (2005-05-30 22:30) [64]

Если же вы все потоки создаете строго из главного, то есть более простое решение, дорабатываем [53]:

 TMyThread = class(TThread)
 private
   fMainThreadId : Cardinal;
 ...
 end;

constructor TMyThread.Create;
begin
 fMainThreadId := GetCurrentThreadId;
 fOwnerHandle := AOwner;
 ...
end;

...

destructor TMyThread.Destroy;
begin
// На случай если Free вызван из-вне
fDestructorPending := True;
while not fCanDestroy do
  begin
    Sleep(100);
    if fMainThreadId = CurrentThreadid then
       Application.ProcessMessages
  end;

 ...
end;
 


 
Mouserx   (2005-05-31 10:08) [65]

Defunct спасибо. С этим понятно.

У меня будет еще вопрос: Как еще можно организовывать двустороний обмен данных между потоками не используя Sinchronize? Стуктура данных в разный момент времени различна.

Если можно - киньте в меня примерами :-)


 
evvcom ©   (2005-05-31 11:09) [66]


> не используя Sinchronize

PostMessage + (возможно) CriticalSection



Страницы: 1 2 вся ветка

Форум: "Основная";
Текущий архив: 2005.06.14;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.64 MB
Время: 0.051 c
1-1117200144
ctranik
2005-05-27 17:22
2005.06.14
Как просканировать весь реестр


1-1117188948
sapsi
2005-05-27 14:15
2005.06.14
архивация текстового файла программно


1-1117185097
electric
2005-05-27 13:11
2005.06.14
Прокрутка в TWebBrowser


4-1114175557
ANB
2005-04-22 17:12
2005.06.14
Как включить/выключить конкретный элемент TCheckListBox


4-1113797567
Zakus
2005-04-18 08:12
2005.06.14
иконка в SysTray.. реакция на события / подсказка в стиле WinXP





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский