std::thread, создание исключения в потоке вызывает ошибку прерывания в Visual C++

Я экспериментировал с std:thread. Я использую двоичное дерево выражений для стандартных арифметических операций. Я создаю поток для выполнения вычислений и хочу проверить деление на ноль. Когда поток запускается с std::async, исключение генерируется из рабочего потока и прекрасно перехватывается в основном потоке. Когда я запускаю поток с помощью std::thread, когда возникает исключение, я получаю ошибку времени выполнения, abort(). Любые идеи относительно того, почему это работает с std::async but notstd::thread`?

// Declaration in the Expression.h file
public:
    static long double __stdcall ThreadStaticEntryPoint(void * pThis);


long double __stdcall Expression::ThreadStaticEntryPoint(void * pThis)
{
    long double calc;

    Expression* pthrdThis = (Expression*)pThis;
    calc = pthrdThis->Calculate();

    return calc;
}

case 6:
    try {
    // Below works when encountering divide by zero.
    // The thrown exception is caught correctly  
    // Launch thread using the this pointer

        std::future<long double> fu = std::async(std::launch::async,
            ThreadStaticEntryPoint, this);
        calc = fu.get();

        // When Creating a thread as below and I throw a divide by zero 
    // exception I get an error in visual C++. Below does not work:

        //std::thread t1(&Expresson::Calculate, this);
        //t1.join();

        // Below works fine also
        //calc = Calculate();
    }
    catch (runtime_error &r)
    {
            ShowMessage("LoadMenu() Caught exception calling Calculate()");
            ShowMessage(r.what());
    }
    catch (...) {
            ShowMessage("Exception caught");
        }

long double Expresson::Calculate()
{
    Expression *e;
    long double calc = 0;

    e = rep->GetExpression();
    if (e == NULL)
    {
        ShowMessage("Main Expression " + to_string(rep->GetMainExpIndex()) + " is NULL. ");
        return 0;
    }

    calc = e->Calculate()

    return calc;
}

//************************************************************
// Calculate - Calculates Lval / Rval, returns result
//************************************************************
long double Divide::Calculate()
{
    Expression* lop = this->getLOperand();
    Expression* rop = this->getROperand();
    long double Lval = 0, Rval = 0;
    long double result = 0;

    if (lop == NULL || rop == NULL)
        return result;

    Lval = lop->Calculate();
    Rval = rop->Calculate();
    //result = divides<long double>()(Lval, Rval);
    // The throw error below causes the error
    if (Rval == 0)
        throw runtime_error("DivExp::Calculate() - Divide by zero exception occured. Rval = 0");

    result = Lval / Rval;

    return result;

}

person Butch Lewis    schedule 26.04.2016    source источник
comment
Почему вы ожидаете, что это сработает? (Я взял работу в кавычки, потому что эта программа работает нормально, просто она не делает то, что вы ожидаете)   -  person user253751    schedule 27.04.2016
comment
Он делает именно то, что ожидается для всех случаев. Только когда я выбрасываю исключение при запуске потока с помощью std::thread, я получаю аварийное завершение() во время выполнения. Если поток запускается с помощью std::async и я выбрасываю исключение, оно перехватывается в основном потоке, как я и ожидал. Почему вы говорите, что это не делает то, что я ожидаю?   -  person Butch Lewis    schedule 27.04.2016
comment
Потому что случай, когда вы выбрасываете исключение, все еще остается случаем, и в этом случае он не делает того, что вы ожидаете...   -  person user253751    schedule 27.04.2016
comment
Ладно, в этом ты прав. Я не могу найти решение, поэтому я подумал, что я что-то упускаю из виду. Кстати, поток std::async работает правильно, не прерываясь, даже когда я вызываю функцию вычисления, как я делаю с std::thread (т.е. std::future‹long double› fu = std::async(std::launch:: async, &Expression::Calculate, this); calc = fu.get();   -  person Butch Lewis    schedule 27.04.2016


Ответы (1)


Это ожидаемое поведение:

См. документацию по стандарту std::thread.

Потоки начинают выполняться сразу после создания связанного объекта потока (в ожидании любых задержек планирования ОС), начиная с функции верхнего уровня, предоставленной в качестве аргумента конструктора. Возвращаемое значение функции верхнего уровня игнорируется, и если она завершается выдачей исключения, вызывается std::terminate.

См. документацию по стандарту std::async.

затем async выполняет функцию f в новом потоке выполнения (со всеми инициализированными локальными потоками), как если бы она была порождена std::thread(f, args...), за исключением случаев, когда функция f возвращает значение или выдает исключение, оно сохраняется в общем состоянии, доступном через std::future, которое async возвращает вызывающей стороне.

person Martin York    schedule 27.04.2016
comment
Вы совершенно правы. Думаю, всегда полезно прочитать инструкцию перед сборкой. У меня всегда оставалась пара винтов или не хватало одного, прежде чем я наконец прочитал инструкции. Спасибо большое. - person Butch Lewis; 27.04.2016