|
Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Теория - multithread & exception-safe |
|
|
Теория - multithread & exception-safe
|
Ветеран Сообщения: 1180 |
Профиль | Отправить PM | Цитировать Доброго времени суток. Чисто теоретический вопрос: пусть есть процесс, который наплодил классов, потом разделился на несколько нитей, каждая из которых наделала своих классов. В некоторый момент в одной нити произошло исключение. Как оно правильно должно быить обработано (имеется ввиду последовательность раскрутки стека).
В однонитевом процессе всё понятно, раскручиваем до ближайшего блока try..catch либо до конца программы. А вот что делать, когда одну из нитей, вызвавшую исключение, докрутим до места её основания? Завершать все остальные нити или прекратить раскрутку? |
|
Отправлено: 15:37, 11-12-2007 |
Старожил Сообщения: 435
|
Профиль | Отправить PM | Цитировать Что вы понимаете под раскруткой?
Добавление в блок catch оператора trow? Если так, то куда его тянуть зависит от логики программы и ни отчего более. IMHO конечно... |
------- Отправлено: 16:20, 11-12-2007 | #2 |
Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети. Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля. |
Ветеран Сообщения: 1180
|
Профиль | Отправить PM | Цитировать Под раскруткой имеется ввиду последовательное разрушение объектов (unwind), например:
class MyForm : public Form { Label label1; Button button1; public: MyForm(); ~MyForm(); void do_something_bad(); }; void MyForm::do_something_bad() { vector<int> vector_of_10_ints(10); // ... // ну ошибся нечаянно vector_of_10_ints.at(5) = vector_of_10_ints.at(15); } void do_something() { MyForm my_form; my_form.do_something_bad(); } порядок раскрутки: раскручивается do_something_bad vector<int>::~vector<int>(); раскручивается do_something MyForm::~MyForm(); Button::~Button(); Label::~Label(); Form::~Form(); т.к. исключение до сих пор не обработано, дальше раскручивается функция, которая вызвала do_something, и т.д. |
Отправлено: 10:21, 12-12-2007 | #3 |
Старожил Сообщения: 435
|
Профиль | Отправить PM | Цитировать Цитата pva:
|
|
------- Отправлено: 13:42, 12-12-2007 | #4 |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать pva, Вообще логично брошенное исключение хоть как-то обработать. У нас во всём, что является многонитевым "корневая" функция потока всегда завёрнута в try{}catch(...){}, который ругается в лог и просто убивает нить, заодно вычеркнув её из пула (нити никогда не завершаются, а сохраняются для следующего задания). Но у нас специфика в том, что есть пул одинаковых обработчиков, и в логику зашито, что некоторые обработчики могут падать, не выдавая, соответственно, никакого результата.
Так что мне кажется, всё зависит от логики программы. Если падает какая-то критичная для работы нить, то уж ничего не попишешь - всё придётся по возможности безболезненно убить. Хотя, в случае гуя, я думаю, библиотека должна быть по максимуму вылизана, а если исключение случается в нити или пользовательском обработчике события, то оно должно быть перехвачено (т.е. стек раскручивается до того места, из которого библиотека вызвала пользовательский код), чтобы вылез попап "произошла неизвестная ошибка, но мы тем не менее продолжаем работать". Примерно так, по-моему, поступает дельфи. А то будет обидно, если в некоем важно приложении криворукий автор не валидирует какое-нибудь текстовое поле перед его использованием и всё падает. Как-то так. |
|
------- Отправлено: 00:44, 13-12-2007 | #5 |
Ветеран Сообщения: 1180
|
Профиль | Отправить PM | Цитировать У меня вообще вопрос пошёл из следующей ситуации (как раз описанной ivank в предыдущем посте): Есть форма, нарисованная на билдере, кнопной, с таймером и запросом. Автоматика такая:
1. Нажали кнопку, она залипла 2. через одну секнду выключилась (disable), запустился запрос 3. запрос отработал, выдал данные, включил кнопка тлипла обратно и включилась теперь в запросе происходит исключение, кнопка не возвращается из своего необычного состояния - потому что нет откатов Вот если бы это всё выполнялось "одной функцией", то есть без разбивки на OnClick и OnTimer, выглядело бы так: struct restore_pushed_t { TButton* button; restore_pushed_t(TButton* b) : button(b) { SendMessage(button->Handle, BM_SETSTATE, BS_PUSHED, 0); } ~restore_pushed_t() { SendMessage(button->Handle, BM_SETSTATE, 0, 0); } struct restore_enabled_t { TButton* button; restore_enabled_t(TButton* b) : button(b) { button->Enabled = false; } ~restore_enabled_t() { button->Enabled = true; } } void Form1::OnClick(TObject* /*sender*/) { restore_pushed_t restore_pushed(Button1); sleep(1000); restore_enabled_t restore_enabled(Button1); ... } 1. Чтобы на время выполнения обработки не заблокировать процесс, вполне логично запихнуть всё это в отдельный TThread. Вроде бы теперь логично обрамлять основной цикл TThread скобками try-catch. А как вести себя, если 2 нити взаимосвязаны и сдой в обной должен разрушить другую? 2. Вариант с использованием событийной системы, чтобы не плодить нити. Разбиваем void Form1::OnClick(TObject* /*sender*/) на части: struct restore_obj { restore_obj() : fprev(gstack_top) { gstack_top = this; } virtual ~restore_obj() { gstack_top = fprev; } static void unwind() throw() { while (gstack_top) delete gstack_top; } private: restore_obj* fprev; static restore_obj* gstack_top; }; struct restore_pushed_t : restore_obj {...} struct restore_enabled_t : restore_obj {...} void Form1::OnClick(TObject* /*sender*/) { try { // включает таймер в конструкторе // выключает в деструкторе new restore_pushed_t(Button1, Timer1); } catch(...) { // log errors restore_obj::unwind(); throw; } } void Form1::OnTimer(TObject* /*sender*/) { try { Timer1->Enabled = false; new restore_enabled_t(Button1); ... restore_obj::unwind(); // вернуть в рабочем режиме } catch(...) { // log errors restore_obj::unwind(); throw; } } |
Отправлено: 07:14, 13-12-2007 | #6 |
Ветеран Сообщения: 1328
|
Профиль | Отправить PM | Цитировать Вот в этих статьях хорошо рассматривается работа SEH Win32, в том числе и в многопоточной среде:
1) http://www.wasm.ru/article.php?article=GordonExcept 2) http://www.wasm.ru/series.php?sid=7 Единственный нюанс: требуется мало-мальское знание Ассемблера. |
------- Отправлено: 04:08, 23-12-2007 | #7 |
Ветеран Сообщения: 1180
|
Профиль | Отправить PM | Цитировать Oleg_SK, в принципе про структурные исключения хорошо расписано в SDK, который идёт с борландовскими компиляторами. Все компиляторы, которые я использовал, очень хорошо справляются с исключениями C++ в плане техники (то есть они отлавливаются). Но одной техники мало. Я приведу описанный выше пример ещё раз (скорее всего непонятно выразился):
Допустим, есть форма. На ней кнопка, таймер и какая-нибудь ерундень типа TPicture. 1. при нажатии кнопки выключаем её Button->Enabled = true и включаем таймер 2. при срабатывании таймера выключаем таймер и загружаем картинку. Потом включаем кнопку Очевидно, что исключение может возникнуть в любом месте программы (по неизвестным причинам). Например файл (картинка) не найден или заблокирован другим процессом. При возникновении исключения в событии 1 после отключения кнопки или в событии 2 до включения кнопки, кнопка остаётся отключенной навсегда. Восстановить ситуацию поможет только перезапуск приложения. А ведь исключения отработали нормально, по правилам и ситуация не такая уж небывалая... Ищу лекарство от таких болезней. |
Отправлено: 20:55, 04-01-2008 | #8 |
Обратный инженер Сообщения: 644
|
Профиль | Отправить PM | Цитировать pva, и всё же я бы советовал прочесть статьи на WASM'е(если вы этого конечно уже не сделали).
Мне терзают сомнения,что стандартные механизмы компиляторов типа try..except обладают достаточной гибкостью по сравнению c установленным вручную SEH. Вы же можете по идее в установленной/ых callback-процедуре/ах делать точную проверку состояния программы.Для этого можно использовать хоть глобальные переменные в секции данных,которые будут выступать в качестве флагов/меток. И после можно будет поправить контекст потока так,чтобы исполнение продолжилось из безопасного места. |
------- Отправлено: 19:34, 05-01-2008 | #9 |
Новый участник Сообщения: 10
|
Профиль | Отправить PM | Цитировать Здравствуйте.
Хочу заметить некоторую особенность многопоточного программироваия. Если в запускаете из потока другой поток, в котором вдруг возникает исключительная ситуация, то отловить её в первом потоке нельзя. Т.е. например для C# если блок try - catch содержит функцию my_thread.Start(), а функция потока my_thread генерирует исключение, то упомянутый блок try - catch его не перехватывает. Для перехвата данного исключения необходимо предусмотреть свой try - catch в обработчике потока my_thread. Если новый поток создаётся по событию из пула потоков, то данные рассуждения применимы и к нему. Удачи. |
Отправлено: 13:31, 02-03-2008 | #10 |
|
Участник сейчас на форуме | Участник вне форума | Автор темы | Сообщение прикреплено |
| |||||
Название темы | Автор | Информация о форуме | Ответов | Последнее сообщение | |
Ошибка - Exception Processing Messag | newxp | Microsoft Windows 2000/XP | 5 | 26-02-2010 10:43 | |
Драйвер - PCI\VEN_1095&DEV_3132&SUBSYS_71321095&REV_01\4&662654C&0&00E0 | kalion-kill | Поиск драйверов, прошивок и руководств | 1 | 08-11-2009 16:45 | |
Win32 exception | __sa__nya | Microsoft Windows NT/2000/2003 | 3 | 16-03-2007 22:05 | |
WinXP & Win2K & Net & Inet проблема!! Help! | SDL2000 | Сетевые технологии | 5 | 19-10-2003 22:29 |
|