Вниз
Скачать: CL | DM;

Как корректно удалить экземпляр TThread из TList?   Найти похожие ветки 

← →
kotyara12   (2010-05-12 21:22) [0]

Может и друрацкий вопрос, но я уже совсем запутался: я создаю несколько независимых потоков с FreeOnTerminate = True и помещаю их в экземпляр TList. Что произойдет, если поток отработает до конца и удалиться из памяти? Произойдет ли при этом удаление соответствущего указателя из TList или нужно как-то по другому удалить отработавший поток?


← →
DVM ©   (2010-05-12 21:37) [1]


> Что произойдет, если поток отработает до конца и удалиться
> из памяти?

Поток отработает, экземпляр TThread будет разрушен. Из TList ссылка на уже несуществующий экземпляр TThread само собой удалена не будет.

Вообще на мой взгляд кривоватый какой то подход ты выбрал.


← →
kotyara12   (2010-05-12 21:49) [2]


> Вообще на мой взгляд кривоватый какой то подход ты выбрал

Как тогда лучше поступить? Подскажи, если не в лом....


← →
kotyara12   (2010-05-12 21:51) [3]

Если при создании потока поставить, FreeOnTerminate = False, то отработавший поток останется в памяти до полного удаления списка, а отследить "извне", отработал поток или еще нет - у меня не получилось.


← →
Anatoly Podgoretsky ©   (2010-05-12 22:01) [4]

> kotyara12  (12.05.2010 21:51:03)  [3]

OnTerminate


← →
kotyara12   (2010-05-12 22:15) [5]


> Anatoly Podgoretsky ©   (12.05.10 22:01) [4]
> > kotyara12  (12.05.2010 21:51:03)  [3]OnTerminate

А не лучше в таком случае в поток при создании передать указатель на List и в Destroy вызвать fList.Remove(Self)?


← →
DVM ©   (2010-05-12 22:16) [6]


> kotyara12   (12.05.10 21:49) [2]

FreeOnTerminate = True - это крайне глюконосная фича, которой на мой взгляд лучше избегать.
Лучше собственноручно отслеживать завершение работы потока и уничтожать объекты TThread.


← →
Rouse_ ©   (2010-05-12 22:51) [7]


> DVM ©   (12.05.10 22:16) [6]
> FreeOnTerminate = True - это крайне глюконосная фича, которой
> на мой взгляд лучше избегать.
> Лучше собственноручно отслеживать завершение работы потока
> и уничтожать объекты TThread.

На основе чего сделан столь сочный вывод? Нет, я конечно понимаю что лучше взять готовый велосипед и немножно его погнуть (читай написать заново уже написанный код), но...?


← →
Игорь Шевченко ©   (2010-05-12 23:06) [8]


> FreeOnTerminate = True - это крайне глюконосная фича, которой
> на мой взгляд лучше избегать.


аргументы будут ? :)


← →
kotyara12   (2010-05-12 23:10) [9]

На данный момент сделал так:
Поток задания:

constructor TAuFpdThread.Create(...);
begin
 inherited Create(True);
 FreeOnTerminate := True;
......


Основной поток:

             TaskItem := TAuFpdThread.Create(...);
             TaskItem.OnTerminate := TerminateTask;
             fTaskList.Add(TaskItem);



procedure TFpdService.TerminateTask(Sender: TObject);
begin
 fTaskList.Remove(Sender);
end;


Думаю, будет работать... завтра проверим :-)


← →
Rouse_ ©   (2010-05-12 23:18) [10]

DVM, я немножко разовью свой предыдущий пост. Сразу предупреждаю - все естественно мое ИМХО.
TThread достаточно кривовато сделанный класс, прежде всего ествественно из-за механизма синхронизации. Мне очень не понятно почему синхронизация нити в классе TThread делается всегда с главной нитью приложения, а не с той нитью из которой был создан данный обьект. Сразу естественно наступаем на грабли в рамках сервисов - главная нить приложения отвечает за общение службы с SCM и соответственно 99.9 процентов времени находится в суспенде.

Что остается - остается только пресловутый FreeOnTerminate, который позволяет нам не заморачиваться на написание кода типа WaitForSingleObject, а просто создать нить - передать ей задачу и забыть.
Если мы отказываемся от данной фичи, то мы имеем на руках:
1: необходимость собственной синхронизации
2. необходимость самостоятельного отслеживания времени жизни нити

Вопрос: зачем тогда он вообще нужен класс этот без использования FreeOnTerminate, если стандартные АПИ гораздо качественней выполняют все описанное? :)


← →
antonn ©   (2010-05-12 23:18) [11]

там Synchronize() случаем не нужен?


← →
Loginov Dmitry ©   (2010-05-12 23:21) [12]


> Думаю, будет работать... завтра проверим :-)


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


← →
Eraser ©   (2010-05-12 23:23) [13]

> [10] Rouse_ ©   (12.05.10 23:18)


> Вопрос: зачем тогда он вообще нужен класс этот без использования
> FreeOnTerminate, если стандартные АПИ гораздо качественней
> выполняют все описанное? :)

кстати, всегда интересовал такой вопрос - где правильно уничтожать дескриптор созданного через Begin/CreateThread потока? )


← →
kotyara12   (2010-05-12 23:23) [14]


> Rouse_ ©

это как раз и есть сервис...


← →
Rouse_ ©   (2010-05-12 23:24) [15]


> Eraser ©   (12.05.10 23:23) [13]

Ествесвенно по завершении оного :)


← →
Loginov Dmitry ©   (2010-05-12 23:24) [16]


> Что остается - остается только пресловутый FreeOnTerminate,
>  который позволяет нам не заморачиваться на написание кода
> типа WaitForSingleObject, а просто создать нить - передать
> ей задачу и забыть.


Вот-вот, передать задачу и забыть. И этом вся прелесть!


← →
Rouse_ ©   (2010-05-12 23:24) [17]


> kotyara12   (12.05.10 23:23) [14]
>
>
> > Rouse_ ©
>
> это как раз и есть сервис...

Тогда ололо - готовь голову к шишкам от граблей :)


← →
kotyara12   (2010-05-12 23:27) [18]


> Вместо TList в таких случаях следует использовать TThreadList

пробовал. наткнулся на следующие грабли: при останвке сервиса останавливаю все потоки задач:

with fTaskList.LockList do
begin
 try
   for i := 0 to Count - 1 do
     TThread(Item[i]).Terminate;
     TThread(Item[i]).WaitFor; <- ЗДЕСЬ ЗАВИСАЕТ НАМЕРТВО, приходится снимать процесс
 finally
   fTaskList.UnlockList;
 end
end


← →
Rouse_ ©   (2010-05-12 23:30) [19]

В рамках сервиса так нельзя - будет дедлок. - Срывай через терминирование.


← →
Rouse_ ©   (2010-05-12 23:31) [20]

Имеется ввиду через апи TerminateThread()


← →
DVM ©   (2010-05-12 23:31) [21]


> Rouse_ ©   (12.05.10 22:51) [7]


> Игорь Шевченко ©   (12.05.10 23:06) [8]

Аргументы чего я должен привести?

Проблема то не в самом FreeOnTerminate = True, а в том, что его применение без особой на то необходимости и без тщательного контроля происходящего принесет только проблемы. Есть некоторая неочевидность того, что происходит при его применении и как пользоваться этим свойством.


← →
Eraser ©   (2010-05-12 23:32) [22]

> [15] Rouse_ ©   (12.05.10 23:24)

да, и потому FreeOnTerminate наше все, т.к. частенько контроль этих самых дескрипторов выливается в куда более не тривиальную задачу, чем сам код потока.
а в случае чего всегда можно FreeOnTerminate отключить, убрать обработчик OnTerminate и дождаться завершения потока через WaitFor.

ну собственно я это не вам рассказываю, а топикстартеру )


← →
kotyara12   (2010-05-12 23:32) [23]


> Rouse_ ©   (12.05.10 23:31) [20]
> Имеется ввиду через апи TerminateThread()

спс... щас покопаемся...


← →
Eraser ©   (2010-05-12 23:33) [24]

> [18] kotyara12   (12.05.10 23:27)

OnTerminate срабатывает? где в этот момент висит TThread(Item[i])?


← →
Rouse_ ©   (2010-05-12 23:34) [25]


> дождаться завершения потока через WaitFor.

Не получиться опять-же, дедлок из-за неочевидного механизма синхронизаии в очень многих случаях :)


← →
DVM ©   (2010-05-12 23:35) [26]


> kotyara12   (12.05.10 23:27) [18]



> with fTaskList.LockList do
> begin
>  try
>    for i := 0 to Count - 1 do
>      TThread(Item[i]).Terminate;
>      TThread(Item[i]).WaitFor; <- ЗДЕСЬ ЗАВИСАЕТ НАМЕРТВО,
>  приходится снимать процесс
>  finally
>    fTaskList.UnlockList;
>  end
> end

Ты свое глюконосное FreeOnTerminate := true убери и не будет виснуть.


← →
Loginov Dmitry ©   (2010-05-12 23:35) [27]


> пробовал. наткнулся на следующие грабли: при останвке сервиса
> останавливаю все потоки задач:


Правильно зависает. Зато ведь понятно из-за чего!

Лобовой метод обхода:

with fTaskList.LockList do
begin
try
  for i := 0 to Count - 1 do
    TThread(Item[i]).Terminate;
finally
  fTaskList.UnlockList;
end
end;

while True do
begin
 with fTaskList.LockList do
 try
    ACount := Count;
 finally
   fTaskList.UnlockList;
 end;
 
 if ACount = 0 then
   Break
 else
 begin
   Sleep(10);
   //Application.ProcessMessages();
 end;
end;


← →
DVM ©   (2010-05-12 23:36) [28]

Убивать потоки принудительно через TerminateThread() недопустимо.


← →
Eraser ©   (2010-05-12 23:37) [29]

> [26] DVM ©   (12.05.10 23:35)

кстати да, тут или FreeOnTerminate := true убрать, или WaitFor;


> [25] Rouse_ ©   (12.05.10 23:34)

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


← →
Rouse_ ©   (2010-05-12 23:38) [30]


> DVM ©   (12.05.10 23:36) [28]
>
> Убивать потоки принудительно через TerminateThread() недопустимо.
>

При завершении приложения? С чего бы это?
И кстати, ты так и не привел аргументов в пользу отсутствия FreeOnTerminate


← →
Rouse_ ©   (2010-05-12 23:39) [31]


> Eraser ©   (12.05.10 23:37) [29]
> так и не чего синхронайз использовать, imho. за редчайшим
> исключением можно обойтись без него

Я бы кнечто тоже за, но ты реальзацию метода WaitFor смотрел? :)
Особливо вызов метода CheckSynchronize в рамках сервиса?


← →
Loginov Dmitry ©   (2010-05-12 23:41) [32]


> Имеется ввиду через апи TerminateThread()


Превосходный совет, нет слов :)


> Проблема то не в самом FreeOnTerminate = True, а в том,
> что его применение без особой на то необходимости и без
> тщательного контроля происходящего принесет только проблемы.
>  Есть некоторая неочевидность того, что происходит при его
> применении и как пользоваться этим свойством.


Тут вроде как всего 2 варианта: либо мы ставим FreeOnTerminate = False, и отвечаем на уничтожение, либо ставим FreeOnTerminate = True, и дальше ни коем образом к объекту потока не обращаемся (ибо он может оказаться уничтожен в любой момент). Кстати да, есть поводы для глюков, как и со Suspend. Может подать идейку чтоб и FreeOnTerminate сделали deprecated =)


← →
DVM ©   (2010-05-12 23:42) [33]


> Rouse_ ©   (12.05.10 23:38) [30]


> При завершении приложения? С чего бы это?

Не, я не про завершение конечно. Хотя...
Я привык и при завершении пытаться все по-человечески все закрывать.


← →
Loginov Dmitry ©   (2010-05-12 23:45) [34]


> > Убивать потоки принудительно через TerminateThread() недопустимо.
>
> >
>
> При завершении приложения? С чего бы это?


С того, что поток в этот момент занят записью какого-нибудь файла на жесткий диск. Убъешь поток, получишь пол-файла или вообще ничего.


← →
Eraser ©   (2010-05-12 23:47) [35]

> [31] Rouse_ ©   (12.05.10 23:39)


> Я бы кнечто тоже за, но ты реальзацию метода WaitFor смотрел?
> :)

ух, ну и накрутили же они, я то наивно пологал, что там честный WaitForSingleObject ;-)
оно кстати так и есть, если GetCurrentThreadID <> MainThreadID, что для сервиса чаще всего и бывает.

 if GetCurrentThreadID = MainThreadID then
 begin
   WaitResult := 0;
   H[1] := SyncEvent;
   repeat
     { This prevents a potential deadlock if the background thread
       does a SendMessage to the foreground thread }
     if WaitResult = WAIT_OBJECT_0 + 2 then
       PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
     WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
     CheckThreadError(WaitResult <> WAIT_FAILED);
     if WaitResult = WAIT_OBJECT_0 + 1 then
       CheckSynchronize;
   until WaitResult = WAIT_OBJECT_0;
 end else WaitForSingleObject(H[0], INFINITE);


← →
Loginov Dmitry ©   (2010-05-12 23:52) [36]

Самое надежное средство: BeginThread. Никогда не подводит :)


← →
Игорь Шевченко ©   (2010-05-13 00:05) [37]

DVM ©   (12.05.10 23:31) [21]


> Аргументы чего я должен привести?


Очевидно глючности конструкции FreeOnTerminate = true, вроде речь шла об этом.


> Проблема .. в том,
> что его применение без особой на то необходимости и без
> тщательного контроля происходящего принесет только проблемы.
>  Есть некоторая неочевидность того, что происходит при его
> применении и как пользоваться этим свойством.


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

Какая нафиг неочевидность - открываешь classes.pas и все, как на ладони.

Потоками вообще надо слегка с умом пользоваться, там и без FreeOnTerminate хватает граблей.


← →
kotyara12   (2010-05-13 00:05) [38]

Спасибо всем за комментарии... начинаю понемногу въезжать в ситуацию.

Насколько я понял из реализации TThread обработка OnTerminate (например, удаление потока из списка) и FreeOnTerminate = True друг другу не мешают... А вызов fTaskList.Remove(Sender); из OnTerminate при FreeOnTerminate = False не уничтожит поток...


var
 FreeThread: Boolean;
begin
 try
   if not Thread.Terminated then
   try
     Thread.Execute;
   except
     Thread.FFatalException := AcquireExceptionObject;
   end;
 finally
   FreeThread := Thread.FFreeOnTerminate;
   Result := Thread.FReturnValue;
   Thread.DoTerminate; <- обработка OnTerminate
   Thread.FFinished := True;
   SignalSyncEvent;
   if FreeThread then Thread.Free; <- FreeOnTerminate = True

или я ошибаюсь?


← →
DVM ©   (2010-05-13 00:07) [39]


> Rouse_ ©   (12.05.10 23:38) [30]


> И кстати, ты так и не привел аргументов в пользу отсутствия
> FreeOnTerminate

1) Мы всегда знаем, сколько у нас живых экземляров объекта TThread
2) Мы всегда можем быть уверены в том что в конкретный момент конкретный экземпляр жив.
3) Мы можем корректно закрыть все ресурсы потоков и сами потоки и затем уничтожить объекты TThread просто вызовом Terminate и WaitFor.

Лучше бы кто нибудь мне привел реальную пользу от FreeOnTerminate


← →
DVM ©   (2010-05-13 00:09) [40]


> Игорь Шевченко ©   (13.05.10 00:05) [37]


> Очевидно глючности конструкции FreeOnTerminate = true, вроде
> речь шла об этом.

Речь не о глючности конструкции, а о ее глюконосности. Разные вещи.



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

Скачать: CL | DM;



Память: 0.56 MB
Время: 0.046 c
2-1274175056
namco
2010-05-18 13:30
2010.08.27
odbc c delphi


4-1232578760
istok2
2009-01-22 01:59
2010.08.27
windows deffender и WriteProcessMemory


15-1269250635
Delphist2
2010-03-22 12:37
2010.08.27
уменьшение размера exe


15-1269434373
Незнайка на Луне
2010-03-24 15:39
2010.08.27
Почему все математики сходят с ума?


2-1268828273
Mark
2010-03-17 15:17
2010.08.27
Помогите решить!




   Наверх