Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Обработка исключения


Автор: Peter 8.1.2006, 22:24
Конкретно дело обстоит так. Многооконное приложение; каждое дочернее окно - объект класса <КлассДокумента>. Когда дочернее окно открыто, в объекте этого класса может выполняться некоторая операция (функция F вызывается по таймеру). В функции F используется переменная x - член класса <КлассДокумента>, имеющий тип "указатель на структуру S". В конструкторе <КлассДокумента>
Код
x = new S;

в деструкторе
Код
x = delete S;

Рассмотрим следующую ситуацию (которая наблюдалась в работе программы): в момент выполнения функции F пользователь закрывает окно. В деструкторе уничтожается объект, на который указывает x, и в то же самое время x используется в F. Если ничего не предпринимать, то программа вылетает.
Может ли что-нибудь испортиться, если я заключу тело F в
Код
try{<тело>}catch(...){}

?????????????? Не повредится ли, например, стэк?

Автор: Fedor 8.1.2006, 22:31
Цитата(Peter @ 8.1.2006, 22:24 Найти цитируемый пост)

в момент выполнения функции F пользователь закрывает окно

может, я туплю, но разве такое может быть? события разве не поочереди выполняются? т.е. сначала до конца до работает функция, а потом только вызовется деструктор? Буду рад, если поправите меня. smile

Автор: Mayk 8.1.2006, 23:07
А может стоит воспользваться чем-нибудь типа shared_ptr, или еще каким имеющимся классом для
подсчета ссылок? В крайнем случае накатать самому.

Цитата(Fedor @ 9.1.2006, 02:31 Найти цитируемый пост)

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

Если приложение многопоточное, и деструктор вызовется не из того потока, в котором выполняется F, то может прийти Большой Ой.
Добавлено @ 23:12
Цитата(Peter @ 9.1.2006, 02:24 Найти цитируемый пост)

1:try{<тело>}catch(...){}

А что нам это даст?

Код

int main()
{
  int *s = new s[10];delete[] s;
  try{s[3]=3;}catch(...){abort();}
}

завершается нормально

Автор: chipset 8.1.2006, 23:21
Курим MSDN на тему синхронизации потоков или используем метод подсчета ссылок как правильно подметил Mayk smile

Автор: Peter 9.1.2006, 12:49
Цитата(Mayk @ 8.1.2006, 23:07 Найти цитируемый пост)
Код
try{<тело>}catch(...){}

А что нам это даст?
Код
int main()    
{    
  int *s = new s[10];delete[] s;    
  try{s[3]=3;}catch(...){abort();}    
}

завершается нормально

Первое нам даст то, что при выполнении F программа не вылетит.
Второе не подходит (наверно, имелось в виду
Код
int *s = new int[10];
), поскольку надо, чтобы корректно закрылось одно дочернее окно, а не все приложение завершило работу.

Синхронизация потоков, конечно, это хорошо, но слишком долго, и в ней нет необходимости, поскольку результат работы функции F пользователя уже не интересует. А поставить try-catch - это дело нескольких секунд. Вопрос лишь в том, не может ли быть от этого побочных эффектов?

Автор: Mayk 9.1.2006, 16:32
Цитата(Peter @ 9.1.2006, 16:49 Найти цитируемый пост)

int *s = new int[10];

Ага

Цитата(Peter @ 9.1.2006, 16:49 Найти цитируемый пост)

Первое нам даст то, что при выполнении F программа не вылетит.

Да ну? Вы СЛИШКОМ высокого мнения об исключениях. В мной указаном примере произошла запись в уже удаленный s. И исключения не возникло(откуда? кто будет кидать исключение? система? а как она узнает что память освобождена?).
Точно так же оно не возникнет если в Вашей программе удалить объект из другого потока:
Цитата(Peter @ 9.1.2006, 02:24 Найти цитируемый пост)

Рассмотрим следующую ситуацию (которая наблюдалась в работе программы): в момент выполнения функции F пользователь закрывает окно. В деструкторе уничтожается объект, на который указывает x, и в то же самое время x используется в F.


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


Кстати. В качестве альтернативы. Грубешая и не правильная синхронизация может выглядеть так:
Код

struct S{
 bool doNotDeleteMe
 ...не важно...
};

void F(S*x){
  x->doNotDeleteMe = true;
  ..не важно..
  x->doNotDeleteMe = false;
}
...
Window::~Window(){
  volatile int& i = const_cast<volatile int&>(s->doNotDeleteMe); 
  while(i); //ждём когда можно удалить
  delete s;
  ...что-то там...
}

И наблюдаем за крахом, в тот момент, когда один поток начнет выполнять F,
прервется до x->doNotDeleteMe = true;, передаст управление другому потоку, который благополучно разрушит x.
ОЙ.

Цитата(Peter @ 9.1.2006, 16:49 Найти цитируемый пост)

Синхронизация потоков, конечно, это хорошо, но слишком долго, и в ней нет необходимости, поскольку результат работы функции F пользователя уже не интересует.

В ней (или в альтернативе из подсчёта ссылок => позднего удаления) есть необходимость. Иначе программа умрёт.

Автор: Earnest 9.1.2006, 17:35
Синхронизация и подсчет ссылок - это не альтернативы, а Необходимые Вещи (причем обе сразу). Впрочем, вместо подсчета ссылок можно придумать какой-нибудь другой механизм управления владением и временем жизни. Но подсчет ссылок - наиболее простой и естественный в данном случае, ИМХО.
Но если есть разные потоки, работающие с одними и теми же данными, в синхронизации всегда есть необходимость!
В данном случае это касается возможной реализации shared_ptr - придется позаботиться о том, чтобы все его методы были потокобезопасны (скажем, инкремент-декремент числа ссылок должен быть атомарным, и т.д.).
Иначе твоя программа будет работать или валиться в зависимости от погоды на Галапагосских островах.

Автор: Peter 9.1.2006, 21:02
Цитата(Mayk @ 9.1.2006, 16:32 Найти цитируемый пост)
Исключение не может быть поймано, если оно не брошено.

Специально для проверки ставил
Код
catch(...){<message-box>}

и сообщение в message-box было благополучно показано, а программа продолжала работать.
Цитата(Mayk @ 9.1.2006, 16:32 Найти цитируемый пост)
В ней (или в альтернативе из подсчёта ссылок => позднего удаления) есть необходимость. Иначе программа умрёт.

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

Автор: threef 9.1.2006, 21:19
Стек не повредится, x указывает на кучу.
Как у тебя происходит управление функцией F , вернее, таймером ? Если таймер привязан к существовании этого дочернего окна, то просто убей его в OnDestroy этого окна. Если у тебя функция по таймеру очень медленная, ( в процессе ее однократного выполнения пользователь может открыть и закрыть окно) , то все равно в однопотоковом приложении этого просто не произойдет smile По определению в одном потоке два сообщения одновременно не обрабатывается, второе ждет себе в очереди. Поэтому, при входе в функцию F проверь x!=NULL, а при удалении окна

Код

delete x;
x=NULL;


Примечание: при использовании многопоточности действительно могут возникнуть всякие ОЙ, этим методом пользоваться нельзя.

Автор: Peter 9.1.2006, 23:01
Приложение многопоточное.
Цитата(threef @ 9.1.2006, 21:19 Найти цитируемый пост)
Стек не повредится, x указывает на кучу.
Спасибо, я и хотел найти подтверждение, что все будет нормально. Вопрос закрыт.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)