Текущий архив: 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