Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

ошибка в уничтожении потока Delphi+Thread+TerminateThread [D6, WinXP]


Страницы:   1 2 3

Delphi5.01 ©   (2006-05-31 06:13:00) 

Хотя в конкретном моем случае речь идет о Delphi, но думаю вопрос больше общий так как касается API функции, которые одинаковые как по синтаксису так и среде использованию для всех (почти) языков. Дело касается потоков и функции TerminateThread(thread.Handle, 0);

Теперь немного о моей программе. В программе есть потоки которые выполняют некую работу. Допустим качают данные с какого-то сайта. Назовем его "поток скачки". Так как у скачки должен быть timeout, я "поток скачки" поместил в другой поток который сделал за временем, и в случае его истечения заверещал "поток скачки" аварийно, TerminateThread("поток скачки".handle, 0); Назовем этот "поток проверки".

В итоге что бы выполнить задания закачки одновременно я создавал несколько "потоков проверки" и ждал пока они все закончат работу. Результат мог быть или положительный, а значит "поток скачки" завершил работу удачно, либо отрицательный, не смогли скачать или кончилось время. После чего данный "поток проверки" убивался также функцией TerminateThread("поток проверки".handle, 0); после чего заново запускался но уже с новыми параметрами.

Но при тестировании и наблюдениями за поведением кода, было замечено что после того как первые 10 "потоков проверки" (а это был массив) завершал работу и запускались новые 10 потоков некоторые вообще исчезали. Другими словами есть подозрение что функция TerminateThread убивала больше одного потока за раз! Так как при подсчете, а я учитывал когда поток заканчивал работу, разница была в 50%. Толи потоки не могли быть убиты, толи наоборот их убили случайно :-)

Одним словом, конечно сложно что-то советовать прочитав выше указанный алгоритм, но может у вас была похожая ситуация? И вообще как можно завершить работу потока аварийно. Что бы наглухо, так как алгоритм Thread.Teriminate не подходит, он завершает работу только после того как внутренние компоненты (созданные в потоке) завершат работу.

Заранее благодарю всех сначала за время потраченное на прочтение такого большого текста и потом за ответ :-)


DVM   (2006-05-31 17:57:00) [21]


> Я использую компонент TIdHTTP для получения информации,
> как уже говорили, есть вероятность что повиснет компонент
> именно при коннекте Как в этом случае его прибить? Может
> просто вызвать IdHTTP.Free? а потом самого потока?

Исходников компонента нет, поэтому можно только гадать, что делается в IdHTTP.Free. CloseSocket() из основного потока решает проблему, как я убедился только что. Но не факт, что закрытие сокета у этого компонента извне не приведет к печальным последствиям.


umbra   (2006-05-31 17:46:00) [22]

так у TIdHTTPесть свойство ConnectTimeout. Выставьте ему приемлемое значение и ловите исключение EIdConnectTimeout


Delphi5.01   (2006-05-31 17:27:00) [23]

Ну ладно если все так легко вы попробуйте считать информацию с внешнего носителя, например CD и потом убить поток! Если информация считывается с поврежденной части CD то нафиг поток не убить! И даже смысла нет давать команду suspend нужно ждать пока сгенериться ошибка I/O и только потом поток завершит работу, а таимаут в таком случае от 30 до 60 секунд, в зависимости от размера считываемой информации. Полтора года назад писал данный код с считыванием ифны, тогда даже о функции TerminateThread-не знал и приходилось до минуты ждать завершения потока.
Теперь вернусь к моему частному примеру. Я использую компонент TIdHTTP для получения информации, как уже говорили, есть вероятность что повиснет компонент именно при коннекте Как в этом случае его прибить? Может просто вызвать IdHTTP.Free? а потом самого потока?


Alex Konshin   (2006-05-31 16:36:00) [24]

> DVM ©   (31.05.06 16:16) [14]
> Попробую привести пример:
> Имеется компонент, получающий данные с сервера. Данные он
> получает в отдельном потоке и потом, скажем отображает.
> Экземпляры этого компонента динамически создаются и уничтожаются
> в процессе работы программы. При уничтожении компонент делает
> потоку Terminate и ждет WaitFor завершения потока. Уничтожить
> себя раньше потока нельзя, так как потоку может что-то понадобиться
> от компонента.
> Имеем: поток висит на функции и ждет таймаута, компонент
> и программа ждут. Все висит.

Остается только задать вопрос: "Кто виноват?"
А зачем ждать по WaitFor? А почему не по WaitForMultipleObjects/WSAWaitFor.../MsgWaitForMultipleObjects?
Почему не иметь событие, которое специально предназначено для вывода из ожидания? Вот и получаем очень простое и надежное решение:
Ждем нескольких событий, одно из которых признак Attention. При выходе из ожидания проверяем код возврата, а также некий флаг (если используется TThread то нужно проверять Terminated). Если от нас хотят завершения, то закрываем все сокеты и все. Все остальные нити завершатся сами собой, правда я все равно с таймаутом жду и уж тогда убиваю.
Естественно, я говорю про Winsock2, т.к. только там есть приемлемое ожидание событий сокета.


DVM   (2006-05-31 16:27:00) [25]


> Alex Konshin ©   (31.05.06 16:17) [15]

Почитал описание на msdn.
Насчет closesocket я был вроде бы не прав. Сейчас проверю на практике.


Eraser   (2006-05-31 16:23:00) [26]


> DVM ©   (31.05.06 16:16) [14]

ситуция понятная и довольно часто встречающаяся, решение - изменение архитектуры, чтобы не было Уничтожить себя раньше потока нельзя, так как потоку может что-то понадобиться от компонента.
Или же перед обращением из потока к полям компонента, проверять, существует ли этот компонент. В свою очередь компонент при уничтожении должен обнулять указатель на себя в экземпляре класса "своего" потока.


Alex Konshin   (2006-05-31 16:17:00) [27]

Я никогда не использую блокирующие сокеты. Так что это твои прблемы.
Почитай внимательно описание closesocket()
http://msdn.microsoft.com/library/en-us/winsock/winsock/closesocket_2.asp
Но, я все равно буду рекомендовать асинхронные сокеты, потому как там все предсказуемо.


DVM   (2006-05-31 16:16:00) [28]


> а зачем вообще немедленно уничтожать поток, который больше
> не нужен?

Попробую привести пример:
Имеется компонент, получающий данные с сервера. Данные он получает в отдельном потоке и потом, скажем отображает.
Экземпляры этого компонента динамически создаются и уничтожаются в процессе работы программы. При уничтожении компонент делает потоку Terminate и ждет WaitFor завершения потока. Уничтожить себя раньше потока нельзя, так как потоку может что-то понадобиться от компонента.
Имеем: поток висит на функции и ждет таймаута, компонент и программа ждут. Все висит.


Сергей М.   (2006-05-31 16:11:00) [29]


> DVM


Блокирующий recv() немедленно вернет управление (с соотв.кодом ошибки), как только в другом (по отношению к данному) потоке будет вызвана closesocket() с указанием хэндла гнезда, "висящего" на recv()

То же самое касается любого другого блок.вызова из состава Winsock.


Eraser   (2006-05-31 16:10:00) [30]


> DVM ©

а зачем вообще немедленно уничтожать поток, который больше не нужен?
допустим при использовании наследников TThread можно просто вызвать метод Terminate, который, в свою очередь, сделает Terminated = true, и почаще проверять этот самый Terminated внутри потока, например сразу после синхронного вызова, и поступать например так:
if Terminated then
 Exit;


DVM   (2006-05-31 16:00:00) [31]


> Создать себе трудности, чтобы потом героически их преодолеть?

Я не люблю неблокирующие сокеты. Это изобретение было специально сляпано для Win3.11 да так и прижилось. Программировать многие задачи с неблокирующими сокетами не легче, а, наоборот, труднее.


DVM   (2006-05-31 15:52:00) [32]


> В данном случае попытайся закрыть сокет

Сокет не закроется, пока recv() не вернет управление, все будет висеть как раньше.


DVM   (2006-05-31 15:50:00) [33]


> Alex Konshin ©   (31.05.06 15:42) [7]


> Это абсолютная ерунда.

Да неужели? Сокеты как я уже написал синхронные. Функция, скажем, recv() не вернет управление, пока не возникнет таймаут, заданный в TTimeVal.tv_sec. И корректно до наступления этого события поток не закрыть. Либо Terminate и ждать, либо уничтожать.

> TerminateThread нужно избегать и использовать только в крайних
> случаях

А я это и не пропагандирую.

Хотелось бы все таки ответ на [6]


Alex Konshin   (2006-05-31 15:45:00) [34]


> DVM ©   (31.05.06 15:34) [6]
> > Это заблуждение.
> Развейте мои заблуждения конкретным примером (кодом). Прежде
> всего интересует код, немедленно останавливающий поток,
> висящий на функциях Winsock.Connect() или Winsock.recv().
>  Сокеты блокирующие.

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


Alex Konshin   (2006-05-31 15:42:00) [35]


> DVM ©   (31.05.06 10:06) [4]
> При работе с сокетами (сетью), к сожалению, нормального
> способа быстро уничтожить поток нет. Если сокет ждет подключения
> (ответа сервера), сервер не отвечает, поток можно будет
> убить только после возникновения таймаута. Многие известные
> программы (в том числе и от MS) уничтожают потоки аварийно,
>  а потом как-то подчищают ресурсы.


Это абсолютная ерунда. Нужно просто правильно писать приложения.
Если использовать синхронную работу с сокетами, то могут быть затруднения ( и то можно забороться), но с асинхронными никаких проблем нет, правда кода писать нужно побольше и повнимательнее.
TerminateThread нужно избегать и использовать только в крайних случаях. Например, я лично не совсем представляю, что будет, если вырубить нить, которая в какой-то критической секции, да и как-то нет желания проверять.
Слишком уж много возможностей самого себя запутать.


DVM   (2006-05-31 15:34:00) [36]


> Это заблуждение.

Развейте мои заблуждения конкретным примером (кодом). Прежде всего интересует код, немедленно останавливающий поток, висящий на функциях Winsock.Connect() или Winsock.recv(). Сокеты блокирующие.


Сергей М.   (2006-05-31 10:08:00) [37]


> DVM ©   (31.05.06 10:06) [4]



> При работе с сокетами (сетью), к сожалению, нормального
> способа быстро уничтожить поток нет


Это заблуждение.


DVM   (2006-05-31 10:06:00) [38]


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

При работе с сокетами (сетью), к сожалению, нормального способа быстро уничтожить поток нет. Если сокет ждет подключения (ответа сервера), сервер не отвечает, поток можно будет убить только после возникновения таймаута. Многие известные программы (в том числе и от MS) уничтожают потоки аварийно, а потом как-то подчищают ресурсы.


Сергей М.   (2006-05-31 08:25:00) [39]


> как можно завершить работу потока аварийно


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

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


Delphi5.01   (2006-05-31 08:01:00) [40]

Сори но кажеться я сам лично нашел ошибку, досканально проверял каждый компонент по отдельности и кажеться ошибка вообше банальная  (в чужом компоненте не доработка )


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо