2008-11-30 7 views

ответ

4

Я считаю, что стандарт C++ не упоминает многопоточность - многопоточность - это специфичная для платформы функция.

Я не совсем уверен, что в стандарте C++ говорится о неперехваченных исключениях в целом, но согласно this page, что происходит, определяется платформой, и вы должны узнать в документации своего компилятора.

В быстром и грязном тесте, который я сделал с g ++ 4.0.1 (i686-apple-darwin8-g ++ - 4.0.1), результатом является то, что вызывается terminate(), который убивает всю программу. Код, который я использовал следующие:

#include <stdio.h> 
#include <pthread.h> 

void *threadproc(void *x) 
{ 
    throw 0; 

    return NULL; 
} 

int main(int argc, char **argv) 
{ 
    pthread_t t; 
    pthread_create(&t, NULL, threadproc, NULL); 

    void *ret; 
    pthread_join(t, &ret); 

    printf("ret = 0x%08x\n", ret); 

    return 0; 
} 

Собран с g++ threadtest.cc -lpthread -o threadtest. Выход был:

terminate called after throwing an instance of 'int' 
+0

Я просто собирался написать то же самое. Просто сделайте более понятным, что приложение закончится (кто-то может понять, что только поток убит с завершением). terminate() приведет к тому, что все потоки будут убиты без дальнейшей обработки. – 2008-11-30 17:38:39

+0

PS. Это поведение всех библиотек pthread, которые я использовал. – 2008-11-30 17:40:04

0

Я не рекомендую оставлять какие-либо исключения посторонними. Оберните функции потока верхнего уровня в обработчиках catch-all, которые могут более изящно (или, по крайней мере, verbosely) закрыть программу.

+0

Хотя это * может * иметь смысл для некоторых приложений, я не согласен с ним в качестве общего совета. Обычно я рекомендую, чтобы люди обрабатывали исключения, когда это имеет смысл, иначе пусть они распространяются. Если вы правильно использовали RAII, вы не должны быть проблемой. – MattyT 2008-11-30 22:46:40

+0

За исключением того, что ваши RAII-деструкторы не могут быть вызваны, если исключение не обнаружено. Реализация заключается в том, удаляет ли неиспользуемое исключение стек или нет. Он может просто прервать. Если вы поймаете все наверху, вы гарантируете стекирование. – 2008-11-30 22:58:55

0

Я думаю, самое главное помнить, что неперехваченные исключения из других потоков не отображаются пользователю или не бросаются в основной поток. Таким образом, вы должны деформировать весь код, который должен выполняться на потоках, отличных от основного потока, с блоками try/catch.

2

Неперехваченное исключение вызовет terminate(), который в свою очередь вызывает terminate_handler (который может быть установлен программой). По умолчанию terminate_handler звонит abort().

Даже если вы переопределяете значение по умолчанию terminate_handler, стандарт говорит, что подпрограмма, которую вы предоставляете, «прекращает выполнение программы, не возвращаясь к вызывающей» (ISO 14882-2003 18.6.1.3).

Итак, вкратце, неперехваченное исключение завершит программу не только потоком.

Что касается безопасности потоков, то как Adam Rosenfield говорит, что это специфичная для платформы вещь, которая не рассматривается стандартом.

2

Это единственная причина, по которой Эрланг существует.

Я не знаю, что такое конвенция, но imho, как можно больше, чем Эрланг. Делайте объекты кучи неизменяемыми и настраивайте какой-то протокол передачи сообщений для связи между потоками. Избегайте блокировок. Убедитесь, что передача сообщений безопасна для исключений. Храните в стеке столько штатных вещей.

2

Как уже обсуждалось, параллелизм (и, в частности, безопасность потоков) является архитектурной проблемой, которая влияет на то, как вы разрабатываете свою систему и свое приложение.

Но я хотел бы задать ваш вопрос о напряженности между безопасностью исключений и безопасностью потока.

На уровне класса для обеспечения безопасности потоков требуются изменения интерфейса. Так же, как безопасность исключений.Например, это обычное для классов, чтобы возвращать ссылки на внутренние переменные, скажем:

class Foo { 
public: 
    void set_value(std::string const & s); 

    std::string const & value() const; 
}; 

Если Foo используется несколькими потоками, беда ждет вас. Естественно, вы могли бы поставить мьютекс или другую блокировку для доступа к Foo. Но достаточно скоро все программисты на C++ захотят обернуть Foo в «ThreadSafeFoo». Мое утверждение, что интерфейс для Foo должен быть изменен на:

class Foo { 
public: 
    void set_value(std::string const & s); 

    std::string value() const; 
}; 

Да, это дороже, но это можно сделать поточно-замками внутри Foo. IMnsHO создает определенное напряжение между безопасностью потока и безопасностью исключений. Или, по крайней мере, вам нужно выполнить больше анализа, поскольку каждый класс, используемый как общий ресурс, должен быть рассмотрен под обоими огнями.

1

Есть два вопроса, которые я заметил:

  • в г ++ на Linux, убийство тредов (pthread_cancel) осуществляется путем выбрасывания «неизвестный» исключение. С одной стороны, это позволяет легко очищать, когда нить убивается. С другой стороны, если вы поймаете это исключение и не свернете его, ваш код заканчивается на abort(). Поэтому, если вы или любой из библиотек, которые вы используете убить темы, вы не можете иметь

    улова (...)

без

throw; 

в многопоточном коде. Here является ссылкой на это поведение в сети:

  • Иногда вам необходимо передать исключение между потоками. Это непростая задача - мы закончили делать некоторые шаги, когда правильное решение - это способ сортировки/демаршаллинга, который вы используете между процессами.
7

C++ 0x будет иметь Language Support for Transporting Exceptions between Threads, так что, когда рабочий поток генерирует исключение, нерестовая нить может поймать или реконструировать его.

Из предложения:

namespace std { 

    typedef unspecified exception_ptr; 

    exception_ptr current_exception(); 
    void rethrow_exception(exception_ptr p); 

    template< class E > exception_ptr copy_exception(E e); 
} 
2

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

Вот как вы поп-то из очереди:

T t; 
t = q.front(); // may throw 
q.pop(); 

Этот интерфейс несколько тупые по сравнению с:

T t = q.pop(); 

Но делается потому, что назначение T копия может бросить. Если копия вызывается после того, как поп произойдет, этот элемент будет потерян из очереди и никогда не сможет быть восстановлен. Но поскольку копия происходит до того, как элемент выскочил, вы можете поместить произвольную обработку вокруг копии из front() в блоки try/catch.

Недостатком является то, что вы не можете реализовать очередь, которая является потокобезопасной, с интерфейсом std :: queue из-за двух шагов. Что хорошо для безопасности исключений (разделение шагов, которые могут быть выбрасываться), теперь плохо для многопоточности.

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

Смежные вопросы