std::thread,在线程中抛出异常会导致 Visual C++ 中的中止错误

Posted

技术标签:

【中文标题】std::thread,在线程中抛出异常会导致 Visual C++ 中的中止错误【英文标题】:std::thread, thowing an exception in thread causes abort error in Visual C++ 【发布时间】:2016-04-26 23:57:45 【问题描述】:

我一直在试验 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;


【问题讨论】:

你为什么期望它“工作”? (我把“工作”放在引号中是因为这个程序运行良好;它只是没有达到你的预期) 它完全符合所有情况的预期。只有当我抛出异常时,当使用 std::thread 启动线程时,我才会得到运行时 abort()。如果线程以 std::async 启动并且我抛出异常,则它会像我预期的那样被捕获在主线程中。为什么你说它没有达到我的预期? 因为你抛出异常的情况仍然是一种情况,在那种情况下它并没有达到你的预期...... 好吧,你说得对。我似乎找不到解决方案,所以我在想我忽略了一些东西。顺便说一句,即使我像使用 std::thread 那样调用计算函数(即 std::future fu = std::async(std::launch:: async, &Expression::Calculate, this); calc = fu.get(); 【参考方案1】:

这是预期的行为:

见std::thread documentation

线程在构造相关线程对象后立即开始执行(等待任何操作系统调度延迟),从作为构造函数参数提供的***函数开始。***函数的返回值被忽略,如果它通过抛出异常终止,则调用 std::terminate

见std::async documentation

然后 async 在一个新的执行线程上执行函数 f(所有线程局部变量都已初始化),就好像由 std::thread(f, args...) 产生一样,除非函数 f 返回一个值或抛出一个异常,它存储在共享状态中,可通过 async 返回给调用者的 std::future 访问。

【讨论】:

你完全正确。我想在组装之前阅读“说明”总是有帮助的。在我最终阅读说明之前,我总是有几颗螺丝遗留下来,或者丢失了一个。非常感谢。

以上是关于std::thread,在线程中抛出异常会导致 Visual C++ 中的中止错误的主要内容,如果未能解决你的问题,请参考以下文章

为啥在构造函数中抛出异常会导致空引用?

在线程中处理在 catch 块中抛出的异常的最佳实践。 (。网)

捕获在不同线程中抛出的异常

从服务器方法中抛出 Meteor.Error 会导致脚本退出

Selenium WebDriver 在线程“main”org.openqa.selenium.ElementNotInteractableException 中抛出异常

使用参数创建时,std::thread 抛出访问冲突异常? [关闭]