Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.07.16;
Скачать: CL | DM;

Вниз

Как правильно удалить поток в различных ситуациях   Найти похожие ветки 

 
Kolan ©   (2006-05-30 19:04) [0]

Здравствуйте,
 Меня мучает вопрос как коректно удалить поток. Под словом удалить подразумеваю вызвать деструктор.

Кое кокие знания у меня есть, но что я делаю не так понять не могу.

1. Вопрос номер один:
   Каким образом удаляют поток?
   - Мой ответ: Нужно вызвать метод Terminate. И незабыть выставить FreeOnTeminate в True
Так?

   - Я знаю, что Terminate просто устанавливает Terminated в True, поэтому его нужно самому проверять.

Если 1 верно(если нет, то где я не прав), то вопрос номер 2:
Как быть в следующих случаях:
а). FreeOnTerminate записано внутри Execute. Поток зоздался спящим, так и небыл "разбужен", и его надо удалить. Те до FreeOnTerminate дело даже не дошло...

б). Поток чего-то ждет. Например ф-цией WaitForSingleObject. И его надо удалить.

в). В потоке используется экземпляр ранее написанного класса. И в этом классе совершаются долгие вычисления. Сатарый класс трогать на хочется. Тогда как быть и как проверять Terminated.

Во всех этих случиях у меня не вызывается деструктор потока...


 
TUser ©   (2006-05-30 19:07) [1]

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


 
Kolan ©   (2006-05-30 19:09) [2]


> TUser ©   (30.05.06 19:07) [1]

Убиваю обычно при закрытии приложения. И MemProof сообщает о не уничтоженных объектах(поля потока) итд...


 
TUser ©   (2006-05-30 19:10) [3]

Самому интересно - будет ли Рихтер против следующего кода

while not Terminated do begin

if WaitForSingleObject (h, 20000) <> WAIT_FAILED then begin

do something

break;

end;

end;

Вроде бы спим и ждем, но все время просыпаемся. Т.е. получается некоторый аналог sleep (20000), хотя и с использованием виндусовской синхронизации.


 
TUser ©   (2006-05-30 19:11) [4]


>
> Убиваю обычно при закрытии приложения. И MemProof сообщает
> о не уничтоженных объектах(поля потока) итд...

OnTerminate? Хотя при закрытии приложения это не важно.


 
Kolan ©   (2006-05-30 19:15) [5]

Все удаление делаю в Destoy, как всегда вообщем.


 
Пусик ©   (2006-05-31 00:00) [6]


> а). FreeOnTerminate записано внутри Execute.


Вариантов масса.

1. Устанавливай  FreeOnTerminate в конструкторе.
2. Последовательность действий:
  - Terminate
  - Resume
3. Не устанавливай FreeOnTerminate в True, а всегда используй:
  - Treminate
  - Resume
  - WaitFor
  - Free
4. Перекрой процедуру Free:
procedure TMyThread.Free(Sender: TObject);
begin
 FreeOnTerminate := True;
 Terminate;
 Resume;
 inherited;
end;


 
Пусик ©   (2006-05-31 00:03) [7]

+ жестокое уничтожение потока:

TerminateThread(MyThread.Handle,0);
MyThread.Free;


 
API ©   (2006-05-31 00:33) [8]

4. Перекрой процедуру Free:
procedure TMyThread.Free(Sender: TObject);
begin
FreeOnTerminate := True;
Terminate;
Resume;
inherited;
end;


Как мило... :)


 
atruhin ©   (2006-05-31 05:50) [9]


>  [6] Пусик ©   (31.05.06 00:00)

Девушка ну не баламутьте людей. Им же жить еще.

>  Kolan ©   (30.05.06 19:04)

Опиши конкретную проблемму если есть. Тогда подскажут. На 5 теоретических вопросов (описанных в любой книге по программированию в windows) вряд ли кто ответит. Описание работы с потоками у Рихтера занимает не менее 100 страниц, ты хочеш чтобы тебе их сюда перепечатали?

> Самому интересно - будет ли Рихтер против следующего кода
> while not Terminated do begin
> if WaitForSingleObject (h, 20000) <> WAIT_FAILED then begin

Не переживай. Увидит повешается! :). В твоем случае при нажатии на кнопку завершения программы придеться ждать 20 сек для выхода из WaitForSingleObject. Думаю более корректный вариант, для выхода из множества потоков, создать Event для выхода и ожидать его WaitForMultipleObjects


 
Пусик ©   (2006-05-31 21:22) [10]


> atruhin ©   (31.05.06 05:50) [9]
>
>
> >  [6] Пусик ©   (31.05.06 00:00)
>
> Девушка ну не баламутьте людей. Им же жить еще.


Предлагаю тебе не болтать ерундой, если нет знаний по теме топика.

> API ©   (31.05.06 00:33) [8]
> Как мило... :)


Спасибо, я тоже так думаю-)


 
tesseract ©   (2006-05-31 21:25) [11]

перекрываем  free ?????

Бакнелл рвёт оставшиеся волосы.......


 
Пусик ©   (2006-05-31 21:25) [12]


> API ©   (31.05.06 00:33) [8]


И в то же время есть еще олдин вариант - менее пугающий для новичков:

procedure TMyThread.Release;
begin
 Terminate;
 FreeOnTerminate := True;
 Resume;
end;


 
Пусик ©   (2006-05-31 21:26) [13]


> tesseract ©   (31.05.06 21:25) [11]
>
> перекрываем  free ?????


А что так волнуешься? Что в этом особенного?


 
Kolan ©   (2006-05-31 21:27) [14]

Все случаи(а, б, в) реальные.
а. Тут пример не нужен.
б. Поток ждет когда в Com порт придет информация.
в. Любой пример в стаом объекте сидит долгий цикл. Рещил завернуть в поток...


 
Пусик ©   (2006-05-31 21:33) [15]


> б. Поток ждет когда в Com порт придет информация.


Два варианта:

1. Разрушить ожидаемый объект(который указан в WaitForSingleObjec). Дальше по накатанной колее.
2. TerminateThread, затем Resume


 
Пусик ©   (2006-05-31 21:33) [16]


> 2. TerminateThread, затем Resume

+ FreeOnTerminate := True;


 
Kolan ©   (2006-05-31 21:38) [17]


> Разрушить ожидаемый объект

Я жду событие... Или вы имеете вв виду объект ядра? Просто CLoseHandle?

> TerminateThread

Как бы жестко убивать не хочется... А что разве в этом случае будет вызван деструктор?


 
Пусик ©   (2006-05-31 21:45) [18]


> Или вы имеете вв виду объект ядра? Просто CLoseHandle?

Я имел ввиду, что надо разрушить объект, дескриптор которого используется в функции ожидания. Ожидание немедленно будет прервано и поток продолжит работу. Дальше уже можно корректно завершать.


> А что разве в этом случае будет вызван деструктор?


Просто после TertminateThread вызови метод Free и все.
В 16 посте лишнее - FreeOnTerminate := True;


 
Kolan ©   (2006-06-01 13:14) [19]

Вот реальная история:
Поток заснул:
if ReadIndex >= WriteIndex then
   begin
     Suspend;
   end


Теперь закрываю приложение:

 FPackageExtractor.Terminate;
 FPackageExtractor.Resume;


По идее должен попрасть в начало Execute:
while not Terminated do (*)
 begin


Ставлб BreakPoint перед закрытием приложения на while not Terminated do.
До точки остонова дело не доходит. Все закрывается Деструктор. Естественно не вызвался.

Что я сделел не так?


 
Пусик ©   (2006-06-01 13:59) [20]


> Что я сделел не так?


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

 if ReadIndex >= WriteIndex then
 begin
    Suspend;
    if Terminated then Break;
 end;


 
Пусик ©   (2006-06-01 14:00) [21]


> По идее должен попрасть в начало Execute:


Не должен, так как выполнение продолжается с места остановки потока.


 
Kolan ©   (2006-06-01 14:02) [22]

if ReadIndex >= WriteIndex then
   begin
     Suspend;
     if Terminated then
     begin
       Break;
     end;
   end


Мой код наналогичный. До Break не добрался, проверял....

PS
Лишние begin..end"ы для отладки .....
Из лога видно что уснул и сё...


 
Пусик ©   (2006-06-01 14:03) [23]


> Из лога видно что уснул и сё...

Ключевая фраза -

> Ставлб BreakPoint перед закрытием приложения


 
Kolan ©   (2006-06-01 14:07) [24]

Ну уснул.
Перед закрытием
 FPackageExtractor.Terminate;
FPackageExtractor.Resume;


Так а где он проснется?


 
Kolan ©   (2006-06-01 14:09) [25]

while not Terminated do
 begin

   if ReadIndex >= WriteIndex then
   begin
     //
     LogManager.WriteString("Suspend");
     //
     Suspend;(*)
     if Terminated then
     begin
       //
     LogManager.WriteString("Break");
     //
       Break;
     end;
   end
   else
   begin
{...}
   end;
 end;
end;

Вот код. Если уснул в (*) и сделать Resume, то где он проснется?


 
Пусик ©   (2006-06-01 14:09) [26]


> Так а где он проснется?


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


 
Пусик ©   (2006-06-01 14:11) [27]

Если ты выполнял Resume, то выполнение продолжится со след. команнды.
В твоем примере это if Terminated then


 
Kolan ©   (2006-06-01 14:11) [28]


>
> Поток проснется на выполнении следующей команды после той,
>  которую закончил выполнять перед усыплением.

Ясно, почему тогда сюда не попадает?


>    //
>      LogManager.WriteString("Break");
>      //
>        Break;


 
Kolan ©   (2006-06-01 14:12) [29]

37 14:11:25 Suspend
Последняя запись в логе...


 
Пусик ©   (2006-06-01 14:12) [30]


> Ясно, почему тогда сюда не попадает?


А ты выполнил Terminate перед Resume?


 
Kolan ©   (2006-06-01 14:16) [31]

procedure TMainForm.FormDestroy(Sender: TObject);
begin
 FReadThread.Terminate;
 FComm.Free;
 FPackageComposer.Free;
 FPackageExtractor.Terminate;
 FPackageExtractor.Resume;

end;

Вот код закрытия формы.


 
begin...end ©   (2006-06-01 14:19) [32]

> Пусик ©   (31.05.06 21:26) [13]

> Что в этом особенного?

То, что Free -- он не виртуальный, вообще-то.


 
Пусик ©   (2006-06-01 14:19) [33]

Я же сказала тебе основную причину, почему ты не видишь в протоколе твоей точки - это то, что ты пытаешься все это проделать при уничтожении главной формы*читай - главного потока). У тебя просто не доходит дело до корректного закрытия доп. потока - система успевает твой дополнительный поток прибить раньше.

Навесь на кнопки все и проверь.


 
Пусик ©   (2006-06-01 14:19) [34]


> begin...end ©   (01.06.06 14:19) [32]
> > Пусик ©   (31.05.06 21:26) [13]
>
> > Что в этом особенного?
>
> То, что Free -- он не виртуальный, вообще-то.


И что? Чему это может помешать?


 
begin...end ©   (2006-06-01 14:21) [35]

> Пусик ©   (01.06.06 14:19) [34]

> Чему это может помешать?

Его перекрытию.


 
Kolan ©   (2006-06-01 14:22) [36]


> У тебя просто не доходит дело до корректного закрытия доп.
>  потока - система успевает твой дополнительный поток прибить
> раньше.
>

Наконецто вот он ответ..... :). Причина ясна. ;)

Как сделать чтобы дошло?

PS
С кнопками работает.


 
Пусик ©   (2006-06-01 14:22) [37]


> begin...end ©   (01.06.06 14:21) [35]
> Его перекрытию.


Это вряд ли-)

Проверь этот код. Все нормально происходит.

procedure TMyClass.Free;
begin
 ShowMessage("!");
 inherited;
end;


 
Kolan ©   (2006-06-01 14:23) [38]


> И что? Чему это может помешать?

В этом случае произайдет закрытие метедоа...


 
Пусик ©   (2006-06-01 14:24) [39]


> Kolan ©   (01.06.06 14:22) [36]


> Как сделать чтобы дошло?


Ожидать завершения потока - метод WaitFor;


 
Пусик ©   (2006-06-01 14:25) [40]


> В этом случае произайдет закрытие метедоа...

-))


 
begin...end ©   (2006-06-01 14:27) [41]

> Пусик ©   (01.06.06 14:22) [37]

Это не перекрытие. А кто этот второй Free вызывать будет?


 
Kolan ©   (2006-06-01 14:27) [42]


> Ожидать завершения потока - метод WaitFor;

Так а если он повис, вдруг я ошибку сделал.. Тогда что при закрытии приложение повиснет? Это еще хуже..

Я вот просто жадержку втавил Sleep(10); и все завершилось...
Как такой вариант? Нормальный?


 
Kolan ©   (2006-06-01 14:30) [43]


> procedure TMyClass.Free;
> begin
>  ShowMessage("!");
>  inherited;
> end;


Попробуй что будет если объявить
var
 MyObj: TObject;
begin
 создать как
 MyObj := TMyClass.Create;
 и вызвать
 MyObj.Free;
end;


 
Пусик ©   (2006-06-01 14:34) [44]


> begin...end ©   (01.06.06 14:27) [41]
> > Пусик ©   (01.06.06 14:22) [37]
>
> Это не перекрытие. А кто этот второй Free вызывать будет?
>


Может быть, термин и неточный, но посмотрю, когда доберусь до компьютера с Delphi.
А вызываться Free вызывается, причем именно так, как от него и ожидается.


> Kolan ©   (01.06.06 14:30) [43]



> Попробуй что будет если объявить
> var
>  MyObj: TObject;
> begin
>  создать как
>  MyObj := TMyClass.Create;
>  и вызвать
>  MyObj.Free;
> end;


Я-то пробовала. Ты лучше сам попробуй;)


 
Пусик ©   (2006-06-01 14:36) [45]


> Kolan ©   (01.06.06 14:30) [43]
>
> > procedure TMyClass.Free;
> > begin
> >  ShowMessage("!");
> >  inherited;
> > end;
>
>
> Попробуй что будет если объявить
> var
>  MyObj: TObject;
> begin
>  создать как
>  MyObj := TMyClass.Create;
>  и вызвать
>  MyObj.Free;
> end;


Честно говоря, не поняла сначала, о чем этот код. Не поняла, для чего приведен пример и после того, как подумала над ним.


 
begin...end ©   (2006-06-01 14:38) [46]

> Пусик ©   (01.06.06 14:34) [44]

Когда доберётесь до компьютера, посмотрите код VCL, если не трудно -- там новые Free не добавляются, а вместо этого перекрывается виртуальный Destroy. Это типа пища для размышления такая.


 
Kolan ©   (2006-06-01 14:39) [47]

type
 TForm1 = class(TForm)
   BitBtn1: TBitBtn;
   procedure FormCreate(Sender: TObject);
   procedure BitBtn1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

 TMyClass = class
   procedure Free;
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin

end;

{ TMyClass }

procedure TMyClass.Free;
begin
 ShowMessage("!");
 inherited;
end;


procedure TForm1.BitBtn1Click(Sender: TObject);
var
 MyObj: TObject;
begin
 MyObj := TMyClass.Create;
 try
 finally
   MyObj.Free;
 end
end;

end.

Пожалуйте.

Компилятор кстати выделеной жирным вообще выбросил...за ненадобностью...


 
Kolan ©   (2006-06-01 14:39) [48]

А если пытаться перекрыть:
TMyClass = class
   procedure Free; override;
 end;

то,
[Error] Unit1.pas(21): Cannot override a static method
...


 
Пусик ©   (2006-06-01 14:43) [49]


> Компилятор кстати выделеной жирным вообще выбросил...за
> ненадобностью...


Ну да. Потому что обращаемся к MyObject как к TObject.
Почему бы не указать компилятору тип объекта явно?

procedure TForm1.BitBtn1Click(Sender: TObject);
var
MyObj: TMyClass;
begin
MyObj := TMyClass.Create;
try
finally
  MyObj.Free;
end
end;


 
Пусик ©   (2006-06-01 14:44) [50]


> begin...end ©   (01.06.06 14:38) [46]
> > Пусик ©   (01.06.06 14:34) [44]
>
> Когда доберётесь до компьютера, посмотрите код VCL, если
> не трудно -- там новые Free не добавляются, а вместо этого
> перекрывается виртуальный Destroy. Это типа пища для размышления
> такая.


Ну и что, что в исходниках VCL не перекрывается этот метод?
Кто-то запрещает это делать?
Посмотреть мне нетрудно, тем более что делаю это постоянно.

А пищу для размышлений обычно более конкретную подкидывают, а не общие фразы. Это так... тоже тема для размышлений.


 
Kolan ©   (2006-06-01 14:51) [51]


> Пусик ©   (01.06.06 14:43) [49]

Вот странно по потокам советуете а основ не знаете :)

> Почему бы не указать компилятору тип объекта явно?

В этом то и смысл перекрытия(полиморфизма).

Классический пример:
Допустим у меня есть 1 предок - фигура:
TShape = class
 procedure Draw; virtual;
end;


И множество потомков(Для примера пусть 2):

TCircle = class(TShape)
 procedure Draw; override;
end;


TSquare = class(TShape)
 procedure Draw; override;
end;


В каждой из перекрытых процедур Draw я напишу конуретную реализацию рисования фигуры.

ТЕперь можно воспользоваться полиморфизмом:

var
 //Объявляю объект - типа предка TShape
 MyObj: TShape;
begin
 // А создаю его как потомка
 MyObj := TCircle.Create;
 // Рисую
 MyObj.Draw;
 // При вызове Draw будет вызвана процедура не предка (TShape), а его потомка(TCircle). Эта произойдет тк процедура драв перекрыта в VMT.  

//Удалю его
 MyObj.Free;
 
 //
 MyObj := TSquare.Create;
 MyObj.Draw;
 // Теперь нарисуется квадрат :)
 MyObj.Free;
end;


 
Kolan ©   (2006-06-01 14:53) [52]


> Ну и что, что в исходниках VCL не перекрывается этот метод?
>
> Кто-то запрещает это делать?


Просто не виртуальный метод НЕльзя перекрыть:
Посмотрите вот это:
http://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F


 
Kolan ©   (2006-06-01 14:53) [53]

Пример на Delphi написан лично мной...


 
Пусик ©   (2006-06-01 15:52) [54]


> Kolan ©   (01.06.06 14:51) [51]
>
>
> > Пусик ©   (01.06.06 14:43) [49]
>
> Вот странно по потокам советуете а основ не знаете :)
>
> > Почему бы не указать компилятору тип объекта явно?
>
> В этом то и смысл перекрытия(полиморфизма).


Да не нужен здесь полиморфизм.
И другие примеры не нужны.

Я ж сказала, неправлильный термин исползовала.
В моем примере метод не перекрывается, а замещается.
И лишь в конце вызывается метод предка.


 
Kolan ©   (2006-06-01 16:01) [55]

Закрывать метод нодо с осторожностью... Не даром компилятор предупреждеие выдает.
В любом случае всех благодарю за ответы. Остался один вопрос:

>
> Я вот просто жадержку втавил Sleep(10); и все завершилось.
> ..
> Как такой вариант? Нормальный?
>


 
Сергей М. ©   (2006-06-01 16:17) [56]


> б). Поток чего-то ждет. Например ф-цией WaitForSingleObject.
>  И его надо удалить


Поток может ждать и [Msg]WaitForMultipleObjects() - функциональность та же, но поток приобретет возможность оперативно реагировать на команды "немедля закругляться по хозяйству"

Можно приспособить и WaitForSingleObject(), опубликовав хэндл объекта синхронизации, сигнала которого ждет поток.

Т.е. просторы для фантазии здесь не ограничены.

Единственное что нельзя сделать - это, например, прервать блок.вызовы синхронных ф-ций пайп-транспорта, если система не Longhorn и не Vista.


 
Пусик ©   (2006-06-01 16:20) [57]


> Как такой вариант? Нормальный?


Не совсем. Поток может не закончиться и через 10, и через 1000мсек.

Лучше так:

var
 RC: DWORD;
begin
...
RC := WaitForSingleObject(MyThread.Handle,1000);
if RC=WAIT_TIMEOUT then TerminateThread(...);
и т. д.


 
Сергей М. ©   (2006-06-01 16:22) [58]


> Пусик ©   (01.06.06 16:20) [57]


Зачем же через секунду сразу убивать поток ?
А вдруг ему требуется еще чуть-чуть до нормального завершения ?


 
Пусик ©   (2006-06-01 16:26) [59]


> А вдруг ему требуется еще чуть-чуть до нормального завершения
> ?
>


Ну так здесь уже дело хозяйское - какое время ожидать завершения. Я только пример привела.



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

Текущий архив: 2006.07.16;
Скачать: CL | DM;

Наверх




Память: 0.61 MB
Время: 0.028 c
15-1149295871
Коды
2006-06-03 04:51
2006.07.16
Что есть виндоссовместимая ОС миниморум?


15-1150417876
tButton
2006-06-16 04:31
2006.07.16
eng->rus


15-1150465092
AlexanderMS
2006-06-16 17:38
2006.07.16
Можно ли оптимизировать данный код?


2-1151514986
NORDmen
2006-06-28 21:16
2006.07.16
Поток в созаднном мной классе


15-1150228324
ReTer
2006-06-13 23:52
2006.07.16
DVD_RW привод читает только cd диски, а dvd перестали работать!





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский