Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Теория - multithread & exception-safe

Ответить
Настройки темы
Теория - multithread & exception-safe
pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Доброго времени суток. Чисто теоретический вопрос: пусть есть процесс, который наплодил классов, потом разделился на несколько нитей, каждая из которых наделала своих классов. В некоторый момент в одной нити произошло исключение. Как оно правильно должно быить обработано (имеется ввиду последовательность раскрутки стека).
В однонитевом процессе всё понятно, раскручиваем до ближайшего блока try..catch либо до конца программы. А вот что делать, когда одну из нитей, вызвавшую исключение, докрутим до места её основания? Завершать все остальные нити или прекратить раскрутку?

Отправлено: 15:37, 11-12-2007

 

Старожил


Сообщения: 435
Благодарности: 63

Профиль | Отправить PM | Цитировать


Что вы понимаете под раскруткой?
Добавление в блок catch оператора trow?
Если так, то куда его тянуть зависит от логики программы и ни отчего более. IMHO конечно...

-------
black-eric.livejournal.com


Отправлено: 16:20, 11-12-2007 | #2



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля.

pva pva вне форума Автор темы

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить 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
Благодарности: 63

Профиль | Отправить PM | Цитировать


Цитата pva:
vector<int> vector_of_10_ints(10); // ... // ну ошибся нечаянно vector_of_10_ints.at(5) = vector_of_10_ints.at(15); »
Это баг. И обработка исключений как ее здесь не ставь не поможет.

-------
black-eric.livejournal.com


Отправлено: 13:42, 12-12-2007 | #4


редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


pva, Вообще логично брошенное исключение хоть как-то обработать. У нас во всём, что является многонитевым "корневая" функция потока всегда завёрнута в try{}catch(...){}, который ругается в лог и просто убивает нить, заодно вычеркнув её из пула (нити никогда не завершаются, а сохраняются для следующего задания). Но у нас специфика в том, что есть пул одинаковых обработчиков, и в логику зашито, что некоторые обработчики могут падать, не выдавая, соответственно, никакого результата.

Так что мне кажется, всё зависит от логики программы. Если падает какая-то критичная для работы нить, то уж ничего не попишешь - всё придётся по возможности безболезненно убить.

Хотя, в случае гуя, я думаю, библиотека должна быть по максимуму вылизана, а если исключение случается в нити или пользовательском обработчике события, то оно должно быть перехвачено (т.е. стек раскручивается до того места, из которого библиотека вызвала пользовательский код), чтобы вылез попап "произошла неизвестная ошибка, но мы тем не менее продолжаем работать". Примерно так, по-моему, поступает дельфи. А то будет обидно, если в некоем важно приложении криворукий автор не валидирует какое-нибудь текстовое поле перед его использованием и всё падает.

Как-то так.

-------
http://ivank.ru


Отправлено: 00:44, 13-12-2007 | #5

pva pva вне форума Автор темы

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить 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);
   ...
}
Теперь что ни случись, кнопка вернётся в своё нормальное состояние, а исключение не убьёт программу, потому что оно отловится снаружи Form1::OnClick борландовской библиотекой. Если что-то вернуться не сможет (в одном из деструкторов вспомогательных классов выплывет исключение), будет автоматически вызвана функция unhandled(), которая вызовет abort().
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;
   }
}
чтобы не писать каждый раз try..catch можно поместить restore_obj::unwind(); в OnException. Вроде теперь всё приятно работает. Но опять возникаеи вопрос: а если я сделаю несколько стеков? в одном произошло исключение, что делать со вторым? как логичнее быть?

Отправлено: 07:14, 13-12-2007 | #6


Аватара для Oleg_SK

Ветеран


Сообщения: 1328
Благодарности: 144

Профиль | Отправить 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

pva pva вне форума Автор темы

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Oleg_SK, в принципе про структурные исключения хорошо расписано в SDK, который идёт с борландовскими компиляторами. Все компиляторы, которые я использовал, очень хорошо справляются с исключениями C++ в плане техники (то есть они отлавливаются). Но одной техники мало. Я приведу описанный выше пример ещё раз (скорее всего непонятно выразился):
Допустим, есть форма. На ней кнопка, таймер и какая-нибудь ерундень типа TPicture.
1. при нажатии кнопки выключаем её Button->Enabled = true и включаем таймер
2. при срабатывании таймера выключаем таймер и загружаем картинку. Потом включаем кнопку
Очевидно, что исключение может возникнуть в любом месте программы (по неизвестным причинам). Например файл (картинка) не найден или заблокирован другим процессом.
При возникновении исключения в событии 1 после отключения кнопки или в событии 2 до включения кнопки, кнопка остаётся отключенной навсегда. Восстановить ситуацию поможет только перезапуск приложения. А ведь исключения отработали нормально, по правилам и ситуация не такая уж небывалая...
Ищу лекарство от таких болезней.

Отправлено: 20:55, 04-01-2008 | #8


Аватара для DillerInc

Обратный инженер


Сообщения: 644
Благодарности: 47

Профиль | Отправить PM | Цитировать


pva, и всё же я бы советовал прочесть статьи на WASM'е(если вы этого конечно уже не сделали).
Мне терзают сомнения,что стандартные механизмы компиляторов типа try..except обладают достаточной гибкостью по сравнению c установленным вручную SEH.
Вы же можете по идее в установленной/ых callback-процедуре/ах делать точную проверку состояния программы.Для этого можно использовать хоть глобальные переменные в секции данных,которые будут выступать в качестве флагов/меток.
И после можно будет поправить контекст потока так,чтобы исполнение продолжилось из безопасного места.

-------
То,что неясно,следует выяснить.То,что трудно творить,следует делать с великой настойчивостью. © Конфуций


Отправлено: 19:34, 05-01-2008 | #9


Новый участник


Сообщения: 10
Благодарности: 2

Профиль | Отправить PM | Цитировать


Здравствуйте.

Хочу заметить некоторую особенность многопоточного программироваия.
Если в запускаете из потока другой поток, в котором вдруг возникает исключительная ситуация, то отловить её в первом потоке нельзя. Т.е. например для C# если блок try - catch содержит функцию my_thread.Start(), а функция потока my_thread генерирует исключение, то упомянутый блок try - catch его не перехватывает. Для перехвата данного исключения необходимо предусмотреть свой try - catch в обработчике потока my_thread. Если новый поток создаётся по событию из пула потоков, то данные рассуждения применимы и к нему.

Удачи.

Отправлено: 13:31, 02-03-2008 | #10



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Теория - multithread & exception-safe

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
Ошибка - 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




 
Переход