Как я могу распространять исключения между потоками?

У нас есть функция, которую вызывает один 9X_thread поток (мы называем это основным потоком). В 9X_cxx теле функции мы создаем несколько рабочих 9X_cpp потоков для выполнения интенсивной работы 9X_exceptions с ЦП, ожидаем завершения всех потоков, а 9X_cross-threading затем возвращаем результат в основной поток.

В 9X_threading результате вызывающий может наивно использовать 9X_thread функцию, а внутри он будет использовать 9X_thread несколько ядер.

Пока все хорошо ..

Наша проблема связана с исключениями. Мы 9X_multithread не хотим, чтобы исключения в рабочих потоках 9X_cxx приводили к сбою приложения. Мы хотим, чтобы 9X_custom-exceptions вызывающий объект функции мог перехватить 9X_multithreading их в основном потоке. Мы должны перехватывать 9X_threading исключения в рабочих потоках и распространять 9X_threads их на основной поток, чтобы они продолжали 9X_threading разворачиваться оттуда.

Как мы можем это сделать?

Лучшее, что я могу 9X_c++ придумать, это:

  1. Перехват целого ряда исключений в наших рабочих потоках (std :: exception и несколько наших собственных).
  2. Запишите тип и сообщение об исключении.
  3. Имейте соответствующий оператор switch в основном потоке, который повторно генерирует исключения любого типа, записанного в рабочем потоке.

Это имеет очевидный недостаток, заключающийся 9X_threading в том, что поддерживает только ограниченный 9X_cross-threading набор типов исключений и требует модификации 9X_multithread при добавлении новых типов исключений.

119
0
9
Общее количество ответов: 9

Ответ #1

Ответ на вопрос: Как я могу распространять исключения между потоками?

В C++ 11 появился тип exception_ptr, который позволяет 9X_threading передавать исключения между потоками:

#include
#include
#include
#include

static std::exception_ptr teptr = nullptr;

void f()
{
    try
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        throw std::runtime_error("To be passed between threads");
    }
    catch(...)
    {
        teptr = std::current_exception();
    }
}

int main(int argc, char **argv)
{
    std::thread mythread(f);
    mythread.join();

    if (teptr) {
        try{
            std::rethrow_exception(teptr);
        }
        catch(const std::exception &ex)
        {
            std::cerr << "Thread exited with exception: " << ex.what() << "\n";
        }
    }

    return 0;
}

Поскольку 9X_threading в вашем случае у вас есть несколько рабочих 9X_thread потоков, вам нужно будет сохранить по одному 9X_threads exception_ptr для каждого из них.

Обратите внимание, что 9X_multithread exception_ptr - это общий указатель, похожий на ptr, поэтому 9X_custom-exceptions вам нужно будет оставить хотя бы один exception_ptr, указывающий 9X_exception на каждое исключение, иначе они будут выпущены.

Специально 9X_threads для Microsoft: если вы используете исключения 9X_custom-exceptions SEH (/EHa), пример кода также будет передавать 9X_exception-handling исключения SEH, такие как нарушения доступа, что 9X_cpp может быть не тем, что вам нужно.

106
0

Ответ #2

Ответ на вопрос: Как я могу распространять исключения между потоками?

В настоящее время единственный переносимый способ - написать 9X_thread предложения catch для всех типов исключений, которые 9X_cpp вы можете передавать между потоками, сохранить 9X_thread информацию где-нибудь из этого предложения 9X_c++ catch, а затем использовать ее позже, чтобы 9X_exceptions повторно выбросить исключение. Это подход, используемый 9X_cross-threading Boost.Exception.

В C++ 0x вы сможете перехватить исключение 9X_multi-threaded с помощью catch(...), а затем сохранить его в экземпляре 9X_c++ std::exception_ptr с помощью std::current_exception(). Затем вы можете повторно загрузить 9X_cpp его позже из того же или другого потока 9X_cross-threading с помощью std::rethrow_exception().

Если вы используете Microsoft 9X_cpp Visual Studio 2005 или новее, то just::thread C++0x thread library поддерживает 9X_exceptions std::exception_ptr. (Отказ от ответственности: это мой продукт).

76
2

  • Теперь это часть C++ 11 и поддерживается MSVS 2010; см. http://msdn.microsoft.com/en-us/ ...

Ответ #3

Ответ на вопрос: Как я могу распространять исключения между потоками?

Если вы используете C++ 11, то std::future может делать 9X_threads именно то, что вы ищете: он может автоматически 9X_multithread перехватывать исключения, которые попадают 9X_c++ в верхнюю часть рабочего потока, и передавать 9X_cxx их родительскому потоку. поток в точке вызова 9X_multi-threaded std::future::get. (За кулисами это происходит точно так 9X_multi-threaded же, как в ответе @AnthonyWilliams; это уже 9X_multithreading было реализовано для вас.)

Обратной стороной 9X_multithread является то, что не существует стандартного 9X_threads способа «перестать заботиться» о std::future; даже 9X_threads его деструктор просто заблокируется, пока 9X_exception задача не будет выполнена. [EDIT, 2017: поведение блокирующего деструктора - это неправильная функция только псевдофьючерсов, возвращаемых из std::async, которую вы ни в коем случае не должны использовать. Обычные фьючерсы не блокируются в их деструкторе. Но вы по-прежнему не можете «отменить» задачи, если используете std::future: задачи, выполняющие обещания, будут продолжать выполняться за кулисами, даже если никто больше не ожидает ответа.] Вот пример Пример 9X_cross-threading игрушки, который может прояснить, что я 9X_cross-threading имею в виду:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

bool is_prime(int n)
{
    if (n == 1010) {
        puts("is_prime(1010) throws an exception");
        throw std::logic_error("1010");
    }
    /* We actually want this loop to run slowly, for demonstration purposes. */
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    for (int i=2; i < n; ++i) { if (n % i == 0) return false; }
    return (n >= 2);
}

int worker()
{
    static std::atomic hundreds(0);
    const int start = 100 * hundreds++;
    const int end = start + 100;
    int sum = 0;
    for (int i=start; i < end; ++i) {
        if (is_prime(i)) { printf("%d is prime\n", i); sum += i; }
    }
    return sum;
}

int spawn_workers(int N)
{
    std::vector> waitables;
    for (int i=0; i < N; ++i) {
        std::future f = std::async(std::launch::async, worker);
        waitables.emplace_back(std::move(f));
    }

    int sum = 0;
    for (std::future &f : waitables) {
        sum += f.get();  /* may throw an exception */
    }
    return sum;
    /* But watch out! When f.get() throws an exception, we still need
     * to unwind the stack, which means destructing "waitables" and each
     * of its elements. The destructor of each std::future will block
     * as if calling this->wait(). So in fact this may not do what you
     * really want. */
}

int main()
{
    try {
        int sum = spawn_workers(100);
        printf("sum is %d\n", sum);
    } catch (std::exception &e) {
        /* This line will be printed after all the prime-number output. */
        printf("Caught %s\n", e.what());
    }
}

Я просто попытался написать 9X_exceptions похожий на работу пример, используя std::thread и std::exception_ptr, но 9X_multithreading что-то пошло не так с std::exception_ptr (с использованием 9X_c++ libc ++), поэтому я еще не получил его, чтобы 9X_c++ он действительно работал. :(

[EDIT, 2017:

int main() {
    std::exception_ptr e;
    std::thread t1([&e](){
        try {
            ::operator new(-1);
        } catch (...) {
            e = std::current_exception();
        }
    });
    t1.join();
    try {
        std::rethrow_exception(e);
    } catch (const std::bad_alloc&) {
        puts("Success!");
    }
}

Понятия не имею, что я делал не так в 2013 году, но уверен, что это была моя вина.]

14
2

  • Хорошая редакция 2017 года. То же, что принято, но с указателем на исключение с огран ...

Ответ #4

Ответ на вопрос: Как я могу распространять исключения между потоками?

Ваша проблема в том, что вы можете получить 9X_threading несколько исключений из нескольких потоков, так 9X_multithreading как каждый может выйти из строя, возможно, по 9X_cross-threading разным причинам.

Я предполагаю, что основной 9X_multi-threaded поток каким-то образом ожидает завершения 9X_thread потоков, чтобы получить результаты, или 9X_multithreading регулярно проверяет ход выполнения других 9X_exceptions потоков, и этот доступ к общим данным синхронизирован.

Простое решение

Простым 9X_cpp решением было бы перехватить все исключения 9X_multithread в каждом потоке, записать их в общую переменную 9X_multithreading (в основном потоке).

После завершения всех 9X_exceptions потоков решите, что делать с исключениями. Это 9X_c++ означает, что все другие потоки продолжили 9X_thread свою обработку, что, возможно, не то, что 9X_cross-threading вам нужно.

Комплексное решение

Более сложное решение состоит 9X_multithreading в том, чтобы каждый из ваших потоков проверял 9X_threading стратегические точки своего выполнения, если 9X_thread исключение было выброшено из другого потока.

Если 9X_multi-threaded поток генерирует исключение, оно перехватывается 9X_exceptions перед выходом из потока, объект исключения 9X_multi-threaded копируется в некоторый контейнер в основном 9X_multithreading потоке (как в простом решении), а для некоторой 9X_multithreading общей логической переменной устанавливается 9X_custom-exceptions значение true.

И когда другой поток проверяет 9X_custom-exceptions это логическое значение, он видит, что выполнение 9X_custom-exceptions должно быть прервано, и прерывает его корректным 9X_custom-exceptions образом.

Когда все потоки были прерваны, основной 9X_cpp поток может обработать исключение по мере 9X_cxx необходимости.

6
0

Ответ #5

Ответ на вопрос: Как я могу распространять исключения между потоками?

Исключение, созданное из потока, не будет 9X_multithread перехвачено в родительском потоке. У потоков 9X_custom-exceptions есть разные контексты и стеки, и, как правило, родительскому 9X_threads потоку не требуется оставаться там и ждать 9X_multithread завершения дочерних процессов, чтобы он 9X_multithread мог перехватить их исключения. Для этой 9X_multithreading уловки в коде просто нет места:

try
{
  start thread();
  wait_finish( thread );
}
catch(...)
{
  // will catch exceptions generated within start and wait, 
  // but not from the thread itself
}

Вам нужно 9X_custom-exceptions будет перехватывать исключения внутри каждого 9X_exceptions потока и интерпретировать статус выхода 9X_cpp из потоков в основном потоке, чтобы повторно 9X_threading генерировать любые исключения, которые могут 9X_threading вам понадобиться.

Кстати, при отсутствии 9X_c++ перехвата в потоке это зависит от реализации, если 9X_cross-threading вообще будет выполняться раскрутка стека, т.е. деструкторы 9X_c++ ваших автоматических переменных могут даже 9X_cpp не вызываться до вызова terminate. Некоторые 9X_exception-handling компиляторы это делают, но это не обязательно.

4
0

Ответ #6

Ответ на вопрос: Как я могу распространять исключения между потоками?

Не могли бы вы сериализовать исключение 9X_cxx в рабочем потоке, передать его обратно в 9X_cxx основной поток, десериализовать и снова 9X_multi-threaded выбросить? Я ожидаю, что для того, чтобы 9X_exception это сработало, все исключения должны быть 9X_multithreading производными от одного и того же класса 9X_multithread (или, по крайней мере, небольшого набора 9X_multithreading классов с оператором switch). Кроме того, я 9X_thread не уверен, что они будут сериализуемыми, я 9X_multi-threaded просто думаю вслух.

3
1

  • @Nawaz, потому что исключение, вероятно, имеет ...

Ответ #7

Ответ на вопрос: Как я могу распространять исключения между потоками?

На самом деле нет хорошего универсального 9X_exception-handling способа передачи исключений из одного потока 9X_cross-threading в другой.

Если, как и должно быть, все ваши 9X_multithread исключения являются производными от std 9X_exception :: exception, тогда у вас может быть общий 9X_exception-handling перехват исключения верхнего уровня, который 9X_threading каким-то образом отправит исключение в основной 9X_cross-threading поток, где оно будет сгенерировано снова. Проблема 9X_multithread в том, что вы теряете точку выброса исключения. Вероятно, вы 9X_exception-handling можете написать код, зависящий от компилятора, чтобы 9X_c++ получать эту информацию и передавать ее.

Если 9X_threads не все ваши исключения наследуют std :: exception, тогда 9X_cxx у вас проблемы и вам нужно написать много 9X_cpp ловушек верхнего уровня в своем потоке ... но 9X_c++ решение все еще остается в силе.

2
0

Ответ #8

Ответ на вопрос: Как я могу распространять исключения между потоками?

Вам нужно будет сделать общий перехват для 9X_exceptions всех исключений в работнике (включая исключения, не 9X_threading являющиеся стандартными, например, нарушения 9X_multithread доступа), и отправить сообщение из рабочего 9X_multithreading потока (я полагаю, у вас есть какой-то обмен 9X_cross-threading сообщениями?) в управляющий поток, содержащий 9X_multithread активный указатель на исключение, и повторно 9X_cpp вызвать его, создав копию исключения. Затем 9X_threading рабочий может освободить исходный объект 9X_cxx и выйти.

1
0

Ответ #9

Ответ на вопрос: Как я могу распространять исключения между потоками?

См. http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html. Также можно написать функцию-оболочку 9X_c++ для любой функции, которую вы вызываете 9X_exception для присоединения к дочернему потоку, которая 9X_cross-threading автоматически повторно генерирует (с помощью 9X_cxx boost :: rethrow_exception) любое исключение, испускаемое 9X_threading дочерним потоком.

1
0