5

您能否告诉我我用来处理用例的方法是否无效,如果是,正确的处理方法是什么:

task<int> do_work(int param)
{
    // runs some work on a separate thread, returns task with result or throws exception on failure
}

void foo()
{
    try
    {
        auto result_1 = do_work(10000);
        auto result_2 = do_work(20000);

        // do some extra work

        process(result_1.get(), result_2.get());
    }
    catch (...)
    {
        // logs the failure details
    }
}

所以代码尝试并行执行两个作业,然后处理结果。如果其中一项作业引发异常,则 calltask::get将重新引发异常。如果两个任务都抛出异常,则会发生此问题。在这种情况下,第一次调用task::get将导致堆栈展开,因此task将调用第二个的析构函数,并反过来导致在堆栈展开期间重新抛出一个异常,从而导致调用“中止”。

在我遇到这个问题之前,这种方法对我来说似乎完全有效。

4

1 回答 1

3

简而言之,您有一个未处理(未观察到)的异常,因为在您的一项任务中引发的异常不会被任务、其延续之一或主应用程序捕获,因为异常是从任务重新抛出的::第一个任务的 get 在第二个任务调用 task::get 之前展开堆栈。

更简化的代码显示std::terminate调用它是因为任务中抛出的异常没有得到处理。取消注释result.get()将阻止对 的调用std::terminate,因为task::get将重新引发异常。

#include <pplx/pplx.h>
#include <pplx/pplxtasks.h>
#include <iostream>

int main(int argc, char* argv[])
{
    try
    {
        auto result = pplx::create_task([] ()-> int
        {
            throw std::exception("task failed");
        });

        // actually need wait here till the exception is thrown, e.g.
        // result.wait(), but this will re-throw the exception making this a valid use-case

        std::cout << &result << std::endl; // use it
        //std::cout << result.get() << std::endl;
    }
    catch (std::exception const& ex)
    {
        std::cout << ex.what() << std::endl;
    }

    return 0;
}

看看中的建议pplx::details::_ExceptionHandler::~_ExceptionHolder()

//pplxwin.h
#define _REPORT_PPLTASK_UNOBSERVED_EXCEPTION() do { \
    __debugbreak(); \
    std::terminate(); \
} while(false)


//pplxtasks.h
pplx::details::_ExceptionHandler::~_ExceptionHolder()
{
    if (_M_exceptionObserved == 0)
    {
        // If you are trapped here, it means an exception thrown in task chain didn't get handled.
        // Please add task-based continuation to handle all exceptions coming from tasks.
        // this->_M_stackTrace keeps the creation callstack of the task generates this exception.
        _REPORT_PPLTASK_UNOBSERVED_EXCEPTION();
    }
}

在原始代码中,第一次调用task::get引发了该任务中引发的异常,这显然阻止了第二次调用,task::get因此第二个任务的异常不会得到处理(仍然“未观察到”)。

将调用第二个任务的析构函数,并反过来导致在堆栈展开期间重新抛出一个异常,从而导致调用“中止”。

第二个任务的析构函数不会重新抛出它只是调用 std::terminate() 的异常(它调用 std::abort())

看。并发运行时的异常处理

于 2014-07-04T11:26:53.887 回答