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

Вниз

Как корректно удалить экземпляр 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 вся ветка

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

Наверх




Память: 0.55 MB
Время: 0.091 c
2-1271164598
Равиль
2010-04-13 17:16
2010.08.27
Не работает SavePictureDialog1.Execute


2-1275324257
И. Павел
2010-05-31 20:44
2010.08.27
Может ли нарушиться Z-последовательность окон?


15-1273012753
Игорь
2010-05-05 02:39
2010.08.27
madCodeHook


2-1265976020
00110011
2010-02-12 15:00
2010.08.27
Дубликаты в списке


2-1273619311
NasdaqPredictor
2010-05-12 03:08
2010.08.27
О написании собственной DLL





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