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
    }
}

そのため、コードは 2 つのジョブを並行して実行し、結果を処理しようとします。ジョブの 1 つが例外をスローした場合、call は例外task::getを再スローします。この問題は、両方のタスクが例外をスローした場合に発生します。この場合、 の最初の呼び出しでtask::getスタックの巻き戻しが発生するため、2 番目のデストラクタtaskが呼び出され、スタックの巻き戻し中に別の例外が再スローされ、'abort' が呼び出されます。

このアプローチは、問題に直面するまでは完全に有効に思えました。

4

1 に答える 1

3

簡単に言えば、タスクの 1 つでスローされた例外がタスク、その継続の 1 つ、またはメイン app によってキャッチされないため、処理されない (観察されない) 例外があります。最初のタスクの get は、2 番目のタスクの task::get への呼び出しが発生する前に、スタックをアンワインドします。

より単純化されたコードはstd::terminate、タスクでスローされた例外が処理されないために呼び出されることを示しています。のコメントを外すと、例外が再スローさresult.get()れるため、への呼び出しが妨げられます。std::terminatetask::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でそのタスクでスローされた例外が発生します。これにより、 への 2 番目の呼び出しが明らかに妨げられるtask::getため、2 番目のタスクの例外は処理されません (「監視されない」ままです)。

2 番目のタスクのデストラクタが呼び出され、スタックの巻き戻し中にもう 1 つの例外が再スローされ、'abort' が呼び出されます。

2 番目のタスクのデストラクタは、std::terminate() (std::abort() を呼び出す) を呼び出すだけの例外を再スローしません。

見る。コンカレンシー ランタイムでの例外処理

于 2014-07-04T11:26:53.887 に答える