Неперехваченное исключение C ++ в рабочем потоке

Неперехваченное исключение ведет себя по-разному для основного потока и другого std :: thread.

вот тестовая программа

#include <thread>

class XXX{
public:
  XXX(){std::fprintf(stderr, "XXX ctor\n");}
  ~XXX(){std::fprintf(stderr, "XXX dtor\n");}
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}
int main(int argc, char *argv[])
{
    if(argc == 1) {
        mytest(0);
    }else{
        std::thread th([&]{mytest(0);});
        th.join();
    }
}

приведенный выше код (C ++ 11), скомпилированный GCC 5.4, запускается без аргументов

XXX ctor
terminate called after throwing an instance of 'std::runtime_error'
   what():  Hello, world!
Aborted (core dumped)

запустить 1 аргумент:

XXX ctor
XXX dtor
terminate called after throwing an instance of 'std::runtime_error'
  what():  Hello, world!
Aborted (core dumped)

Итак, раскрутка стека выполняется в рабочем потоке, но не в основном потоке, ПОЧЕМУ?

Я спрашиваю, потому что я хотел бы, чтобы дамп ядра давал полезную информацию о трассировке стека в обоих случаях (для неперехваченного исключения).

Заранее спасибо!!!


Дальнейшие испытания показывают, что добавление ключевого слова noexcept в тело функции потока mytest () может частично решить мою проблему, потому что раскрутка не удастся, но это не лучшее решение, потому что раскрутка все равно будет частично происходить, если mytest () вызывает другой функция без гарантии noexcept и фактически сгенерировала неперехваченное исключение.


Обновление: спасибо всем поставщикам комментариев, теперь я понимаю, что исключение C ++ не поддерживает обратную трассировку, и GCC, как реализация C ++, имеет свободу выбора не раскручиваться, когда неперехваченное исключение выбрасывается из основного потока, и раскручиваться, когда из рабочего потока.


Обновление: особая благодарность Сиду С и Джайву Дадсону, я должен перепутать некоторые концепции: 1) обработка исключений / ошибок; 2) утверждение во время выполнения 3) Ошибка сегмента, 2 и 3 аналогичны, они являются невосстановимыми ошибками, немедленное прерывание - единственный выбор, они также удобны для обратной трассировки в отладчике, потому что раскрутка стека не задействована. они вообще не должны реализовываться с использованием концепции исключений. Предполагается, что исключение будет перехвачено всегда, позвольте неперехваченному исключению, оставить main () не рекомендуется.


person TingQian LI    schedule 31.01.2018    source источник
comment
Связанный вопрос: stackoverflow.com/ вопросы / 7730502 /   -  person veefu    schedule 31.01.2018
comment
Еще связанный вопрос: stackoverflow.com/questions/ 2443135 / how-do-i-find-where-an-exception-was-thrown-in-c   -  person TingQian LI    schedule 31.01.2018


Ответы (2)


Почему? Просто так оно и есть. Начиная с C ++ 11, существует некоторая поддержка обработки исключений, возникающих в потоках, отличных от main, но вам нужно будет настроить потоки для перехвата исключений и их повторной генерации. Вот как.

#include <thread>
#include <iostream>

class XXX {
public:
    XXX() { std::fprintf(stderr, "XXX ctor\n"); }
    ~XXX() { std::fprintf(stderr, "XXX dtor\n"); }
};

void mytest(int i)
{
    XXX xtemp;
    throw std::runtime_error("Hello, world!");
}

int main(int argc, char *argv[])
{
    std::exception_ptr exception_ptr = nullptr;
    if (argc == 1) {
        mytest(0);
    }
    else {
        std::thread th([&exception_ptr]() {
            try {
                mytest(0);
            }
            catch (...) {
                exception_ptr = std::current_exception();
            }
        });
        th.join();
        if (exception_ptr) {
            try {
                std::rethrow_exception(exception_ptr);
            }
            catch (const std::exception &ex)
            {
                std::cerr << "Thread exited with exception: " << ex.what() << "\n";
            }
        }
    }
}
person Jive Dadson    schedule 31.01.2018
comment
Спасибо за ваш код, он проиллюстрировал хороший способ использования исключения. Но когда мы запускаем этот код внутри отладчика, такого как GDB, происходит исключение, мы теряем контекст (какой фрагмент кода генерирует) к тому времени, когда GDB останавливается, верно? - person TingQian LI; 31.01.2018
comment
Поэтому поставьте точку останова на строке, содержащей exception_ptr = std :: current_exception (). - person Jive Dadson; 31.01.2018
comment
Я говорю, что перехватить каждое исключение и отобразить сообщение об этом менее полезно для определения причины исключения, и поэтому неперехваченное исключение более благоприятно для меня, и хорошие новости - это его работа в основном потоке, плохие новости - это его не работает в рабочем потоке. Хорошо, может быть, я ожидаю неправильного ответа от исключения C ++ - person TingQian LI; 31.01.2018
comment
поместите точку останова в строку, содержащую exception_ptr = std :: current_exception (), также не сообщайте нам, какая строка внутри mytest (0) вызывает исключение --- предположим, что mytest () намного сложнее, чем здесь - person TingQian LI; 31.01.2018
comment
Я думаю, вам просто нужно научиться лучше пользоваться отладчиком. Я не знаком с GDB, но с Visual Studio вы можете проверять стек, когда поток достигает точки останова. - person Jive Dadson; 31.01.2018
comment
Проверьте параметры отладчика. Я не знаю о GDB, но несколько отладчиков Windows, с которыми я работал, имели возможность приостанавливать выполнение при выбросе любого / выбранного исключения. Это даст вам нужный контекст. - person veefu; 31.01.2018
comment
Спасибо за ваш комментарий, я нашел эту команду в GDB (gdb) catch throw, работает, но она улавливает весь бросок, а не только неперехваченный бросок - person TingQian LI; 31.01.2018

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

person Sid S    schedule 31.01.2018
comment
Моя логика заключается в том, что перехват всех возможных исключений бесполезен для целей отладки, поэтому я разрешаю не перехваченным исключениям действовать как какое-то утверждение времени выполнения, но лучше, чем assert, если сайт вызова обрабатывает некоторые исключения, эти исключения не будут действует как assert автоматически. - person TingQian LI; 31.01.2018
comment
Это просто не лучший способ справляться с исключениями. Если вы их бросаете, вы должны ловить их - в одном потоке исполнения. - person Sid S; 31.01.2018
comment
Хорошо, я, должно быть, слишком многого жду от механизма исключений C ++, :) - person TingQian LI; 31.01.2018
comment
Итак, вы говорите, что исключение - это не механизм отладки, по крайней мере, не предназначенный для этой цели? поэтому я должен поймать все возможные исключения (по крайней мере, во внешней функции), если я решил использовать его? @Sid S - person TingQian LI; 31.01.2018
comment
@TingQianLI, Да, правильно. Если вы генерируете исключение, вы должны где-нибудь его перехватить, независимо от того, отлаживаете ли вы свою программу или нет. Если вы попытаетесь открыть файл, а его там нет, это будет исключением из нормального выполнения вашей программы. Ширли, вы бы хотели, чтобы ваша программа справилась с этой ситуацией? - person Sid S; 31.01.2018
comment
Спасибо за поправку, я перепутал некоторые понятия: 1) обработка исключений / ошибок; 2) утверждение времени выполнения 3) Сбой сегмента 2 и 3 аналогичны, они являются невосстановимыми ошибками, немедленное прерывание - единственный выбор, они также подходят для обратной трассировки в отладчике. они вообще не должны реализовываться с использованием концепции исключений. - person TingQian LI; 31.01.2018
comment
Большое спасибо, @Sid S - person TingQian LI; 31.01.2018