| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Exception в Thread |
| Автор: cemick 21.1.2009, 11:57 |
| Помогите разобратся, в процесс работы с потоками появился вопрос: Что происходит с исключениями возбужденными в Tthread.Execute? |
| Автор: Matematik 21.1.2009, 12:14 | ||
Поток завершается. Далее исключение можно отловить в событии Tthread.OnTerminate()
AFAIK, могу и ошибаться |
| Автор: CodeMonkey 21.1.2009, 12:16 |
| Всё верно. + ещё: Если свойство потока FreeOnTerminate ложно, то FatalException можно ещё прочитать после завершения потока откуда угодно - не обязательно из OnTerminate - пока объект потока не будет удалён. |
| Автор: cemick 21.1.2009, 12:54 |
| Спасибо, помогли. |
| Автор: JSinx 4.2.2009, 23:44 | ||||
Странно, но у меня он равен nil, даже если возникало исключение, хотя при создании потока делаю FreeOnTerminate := false; Собственно мне нужно сделать обработку исключения из потока в главной форме, но при вызове raise у меня выводит кучу ошибок. т.е. вот такой код:
показывается еще раз окно с исключением, но потом уже совсем левые ошибки среды идут :(( |
| Автор: CodeMonkey 5.2.2009, 11:26 | ||||||||
Ать-ать (бьёт по рукам). Нельзя так. Могли бы и сами догадаться. Ведь обычно проверкой FatalException никто не заморачивается. Что это значит? А то, что по-умолчанию FatalException освобождается при удалении экземпляра TThead. С другой стороны, передавая экземпляр объекта в raise вы отдаёте управление сроком его жизни в рамки стандартного механизма исключений. Вот у вас и подрались два механизма. Правильный вариант (например):
Или (более вкусный, но грязный):
Добавлено через 4 минуты и 26 секунд Да, и кстати, возбуждать исключение в OnTerminate - крайне плохая идея. Если вы посмотрите на исходники TThread, то увидите:
Правильный вариант - сделать PostMessage в главную форму и в обработчике сообщения уже выполнить все свои грязные действия. |
| Автор: JSinx 5.2.2009, 23:39 | ||
Увидел в примере какой то книжки вот такую обработку исключений в потоках:
Такой метод чемнить плох? |
| Автор: CodeMonkey 6.2.2009, 10:00 |
| Вроде бы в этом примере всё чисто и красиво. |
| Автор: SyCoDeR 6.2.2009, 10:26 | ||||||||||
Вступлю в дискуссию, ибо сам столкнулся с этой проблемой.
Тут вы видимо правы. Но вот это
не работает. И это
к сожалению тоже. Вообще совершенно любая попытка вызвать исключение в обработчике OnTerminate даже никак не связанное с исходным - например так
Вызывает падение программы. К сожалению, я не достаточно понимаю механизмы работы исключений - они достаточно сложны и абсолютно не документированы. А исходники написаны на ASM'е и, что хуже, опираются на недокументированные структуры данных. Если кто-нибудь в деталях объяснит, почему нельзя делать Raise в OnTerminate буду чрезвычайно благодарен. А пока - обходной путь:
|
| Автор: CodeMonkey 6.2.2009, 14:04 |
Это http://delphikingdom.ru/asp/answer.asp?IDAnswer=65697. Система завершает всё приложение целиком, если в любом из его потоков возникает необработанное исключение. Системная функция CreateThread создаёт голый поток - вы обязаны сами заключать его код в try/except. BeginThread - это обёртка вокруг CreateThread, которая (помимо всего прочего) устанавливает фрейм исключения. По исключению в потоке должен вызываться обработчик исключений из SysUtils (если я правильно помню). Проблема в том, что в некоторых версиях Delphi это поведение реализовано некорректно. Ну а TThread просто использует BeginThread для вызова функции Execute. Поскольку OnTerminate вызывается вне глобального обработчика исключений на Execute, то и исключение в нём ничем не обрабатывается, кроме оболочки BeginThread. Которая, как уже было сказано, работает не всегда. Соответственно, ваше приложение может работать (показывая сообщение об ошибке) или вылетать - смотря в какой версии Delphi оно скомпилировано. +1 причина не делать ничего такого в OnTerminate ;) |
| Автор: SyCoDeR 6.2.2009, 14:48 | ||
| Во всяком сулчае в CodeGear™ Delphi® 2007 for Win32® Version 11.0.2902.10471 гарантированно вылетает. Если кто-то может протестить на других версиях - вот код:
Обратите внимание, вылет в дебагер (разумеется, если в опциях включено Stop on delphi exceptions) на 'Вызываем новое исключение.' будет два!! раза. Собственно, я думаю, что это еще одно проявление той же ошибки. Разобраться бы еще какой... |
| Автор: MetalFan 6.2.2009, 16:28 |
| механизм вызова метода, назначенного св-ву OnTerminate можно подсмотреть в VCL. код в OnTerminate через механизм синхронизации выполняется в контексте главного потока, НО обрабатываться исключение должно в контексте потока, вызвавшего этот метод через Synchronize да и вообще вызов необработанного исключения в любом потоке приведет к падению программы... Добавлено @ 16:36 кстати, знает ли общественность об еще одном механизме вызова метода в контексте осн.потока VCL? см. TThread.Queue |
| Автор: CodeMonkey 8.2.2009, 12:08 |
| Кто-то уже подсуетился: http://qc.codegear.com/wc/qcmain.aspx?d=71230. |
| Автор: MetalFan 8.2.2009, 13:03 |
| да это не бага, это нормальное и логичное поведение, если посмотреть код ThreadWrapper в classes.pas |
| Автор: CodeMonkey 8.2.2009, 19:50 |
| Так я и не спорил |
| Автор: JSinx 10.2.2009, 19:44 | ||||
Попытаюсь подвести итог
и "вариант из книги"
Какой же из них тогда более правильный? P.S. За "вариант из книги" почему-то проминусили |
| Автор: CodeMonkey 10.2.2009, 20:28 |
| >>> Какой же из них тогда более правильный? Оба правильные. Просто работают по-разному. Хотя сильно принципиальной разницы между ними нет. |
| Автор: cemick 4.2.2013, 12:31 |
| На правах автора темы подниму из небытия. Интересно, что если в не основном потоке будет try // Код потока except // Сохраняем исключение в поле FExceptionObject := AcquireExceptionObject(); end; А в главном потоке сделаем: raise FThread.ExceptionObject ; Как мне видеться такой код чист и должен работать или я ошибаюсь? |