インテル® C++ コンパイラー 17.0 デベロッパー・ガイドおよびリファレンス

ストランド境界を超えるロックの保持

ストランド境界を越えるロックの保持を回避することは、最も確実で簡単な方法です。同じレベルのストランドは同じロックを使用することができますが、親と子が同じロックを共有すると、次のような潜在的な問題が起こります。

次のシリアルコードについて考えてみます。

#include <cilk/cilk.h>
#include <tbb/mutex.h>
#include <iostream>
void child (tbb::mutex &m, int &a)
{
   m.lock();
   a++;
   m.unlock();
}

void parent(int a, int b, int c)
{
   tbb::mutex m;
   try
   {
     cilk_spawn child (m, a);
     m.lock();
     throw a;
   }
   catch (...)
   {
     m.unlock();
   }
}

このコードには、cilk_spawn を含む try ブロックの最後に暗黙的な cilk_sync があります。 例外が発生すると、すべての子が完了するまで実行を続行することができません。親が子よりも先にロックを取得した場合、すべての子が完了するまで catch ブロックを実行できず、一方、子はロックを取得するまで完了できないため、デッドロックが発生します。この場合、"ガード" または "スコープロック" オブジェクトを使用しても、ガード・オブジェクトのデストラクターは catch ブロックが終了するまで実行されないため意味がありません。

さらに、このコードには不可視の try ブロックがあります。 自明でない (non-trivial) デストラクターとしてローカル変数を宣言する複合文には、暗黙的な try ブロックが含まれています。そのため、プログラムはスポーンまたはロックを取得する前に、すでに try ブロックを開始している可能性があります。

関数に子によって取得されたロックがある場合、その関数はロックをリリースする前に例外をスローすべきではありません。ただし、ほとんどの関数は例外をスローしないことを保証できないため、次の規則に従います。