方法: 例外処理を使用する OpenMP ループを変換し、コンカレンシー ランタイムを使用する
この例では、例外処理を実行する OpenMP parallelfor ループを変換して、同時実行ランタイムの例外処理機構が使用されるようにする方法を示します。
OpenMP では、並列領域でスローされた例外は、同じスレッドによって同じ領域でキャッチおよび処理される必要があります。 例外が並列領域をエスケープする場合は、ハンドルされない例外のハンドラーがそれをキャッチし、既定ではプロセスを終了します。
同時実行ランタイムでは、タスク グループ (concurrency::task_group オブジェクトや concurrency::structured_task_group オブジェクトなど)、または並列アルゴリズム (例: concurrency::parallel_for) に渡す処理関数の本体で例外をスローすると、ランタイムはその例外を保存し、タスク グループまたはアルゴリズムが終了するまで待機しているコンテキストにその例外をマーシャリングします。 タスク グループの場合、待機しているコンテキストは、concurrency::task_group::wait、concurrency::structured_task_group::wait、concurrency::task_group::run_and_wait、または concurrency::structured_task_group::run_and_wait を呼び出すコンテキストです。 並列アルゴリズムの場合、待機しているコンテキストは、そのアルゴリズムを呼び出したコンテキストです。 また、ランタイムは、タスク グループ内のすべてのアクティブ タスク (子タスク グループ内のタスクも含む) を中断すると共に、開始されていないすべてのタスクを破棄します。
例
この例では、OpenMP parallel
領域および parallel_for
の呼び出しで例外を処理する方法を示します。 do_work
関数は、失敗して std::bad_alloc 型の例外をスローするメモリ割り当て要求を実行します。 OpenMP を使用するバージョンでは、例外をスローするスレッドは例外のキャッチも行う必要があります。 つまり、OpenMP 並列ループの各反復で例外が処理される必要があります。 コンカレンシー ランタイムを使用するバージョンでは、メイン スレッドは別のスレッドによってスローされる例外をキャッチします。
// concrt-omp-exceptions.cpp
// compile with: /EHsc /openmp
#include <ppl.h>
#include <new>
#include <iostream>
#include <sstream>
using namespace concurrency;
using namespace std;
// Demonstrates a function that performs a memory allocation request
// that does not succeed.
void do_work(int)
{
// The following memory allocation causes this function to
// throw std::bad_alloc.
char* ptr = new char[(~unsigned int((int)0)/2) - 1];
// TODO: Assuming that the allocation succeeds, perform some work
// and free the allocated memory.
delete[] ptr;
}
// Demonstrates an OpenMP parallel loop that performs exception handling.
void omp_exception_handling()
{
#pragma omp parallel for
for(int i = 0; i < 10; i++)
{
try {
// Perform a unit of work.
do_work(i);
}
catch (exception const& e) {
// Print the error to the console.
wstringstream ss;
ss << L"An error of type '" << typeid(e).name()
<< L"' occurred." << endl;
wcout << ss.str();
}
}
}
// Demonstrates an Concurrency Runtime parallel loop that performs exception handling.
void concrt_exception_handling()
{
try {
parallel_for(0, 10, [](int i)
{
// Perform a unit of work.
do_work(i);
});
}
catch (exception const& e) {
// Print the error to the console.
wcout << L"An error of type '" << typeid(e).name()
<< L"' occurred." << endl;
}
}
int wmain()
{
wcout << L"Using OpenMP..." << endl;
omp_exception_handling();
wcout << L"Using the Concurrency Runtime..." << endl;
concrt_exception_handling();
}
この例を実行すると、次の出力が生成されます。
Using OpenMP...
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
An error of type 'class std::bad_alloc' occurred.
Using the Concurrency Runtime...
An error of type 'class std::bad_alloc' occurred.
この例に示す OpenMP を使用するバージョンでは、各ループ反復で例外が発生し、処理されます。 コンカレンシー ランタイムを使用するバージョンでは、ランタイムが例外を保存し、すべてのアクティブ タスクを停止すると共に、まだ開始されていないタスクを破棄し、parallel_for
を呼び出すコンテキストに例外をマーシャリングします。
OpenMP を使用するバージョンを例外発生後に終了する必要がある場合は、ブール型のフラグを使用して、エラーが発生したことを他のループ反復に通知します。 キャンセル処理を使用する OpenMP ループを変換し、同時実行ランタイムを使用する方法に関するトピックの例に示すとおり、フラグが設定されている場合、それ以降のループ反復では何も行われません。 一方、コンカレンシー ランタイムを使用するループを例外発生後に続行する必要がある場合は、並列ループの本体で例外を処理します。
非同期エージェントや軽量タスクなど、コンカレンシー ランタイムの他のコンポーネントでは、例外は転送されません。 代わりに、ハンドルされない例外のハンドラーが例外をキャッチし、既定ではプロセスを終了します。 例外処理の詳細については、「例外処理」を参照してください。
parallel_for
および他の並列アルゴリズムの詳細については、「並列アルゴリズム」を参照してください。
コードのコンパイル
コード例をコピーし、Visual Studio プロジェクトに貼り付けるか、concrt-omp-exceptions.cpp
という名前のファイルに貼り付けてから、Visual Studio のコマンド プロンプト ウィンドウで次のコマンドを実行します。
cl.exe /EHsc /openmp concrt-omp-exceptions.cpp