2

次のマルチスレッド コード内に問題はありますか? いつも一貫性のない結果が得られます。コンパイラの最適化により、フラグ設定行がデータ処理行の前に移動され、深刻なデータ競合状態が発生する可能性があるようです。

バリアを追加せずにこれを回避する方法はありますか?

#pragma omp parallel num_threads(16)

int tid=omp_get_thread_num();

if (tid<8)
{
   copydata(arrayofPtrs[tid]);

   flag[tid]=1;//flag is an array of volatile int where its initial values are all 0.

}
else
{
   for (int i=0; i<100000; ++i)
   {
     if (flag[tid-8]==1)
      {
       processingdata(arrayofPtrs[tid-8]);
       break;
       }
     else
       Sleep(200);
   };
};
4

2 に答える 2

0

私があなたのコードを理解できる限り、データがコピーされない限りデータの処理を続行できないため、並行して実行する意味がありません。処理スレッドは、コピースレッドが終了して設定されるのを待ってCPU時間を無駄にします.国旗。次に、両方の操作を 1 つのブロックにマージしてみませんか。

#pragma omp parallel num_threads(8)
{
   int tid = omp_get_thread_num();

   copydata(arrayofPtrs[tid]);
   processingdata(arrayofPtrs[tid]);
}

元のアイデアを保持したい場合、おそらくコピーと処理の両方が非同期で繰り返し行われる場合は、Open MPatomic操作を使用してフラグへのアクセスを同期する必要があります。

#pragma omp parallel num_threads(16)
{
   int tid = omp_get_thread_num();

   if (tid < 8)
   {
      copydata(arrayofPtrs[tid]);

      #pragma omp atomic write
      flag[tid] = 1;//flag is an array of volatile int where its initial values are all 0.
   }
   else
   {
      for (int i = 0; i < 100000; ++i)
      {
         #pragma omp atomic read
         int ready = flag[tid-8];
         if (ready == 1)
         {
            processingdata(arrayofPtrs[tid-8]);
            break;
         }
         else
            Sleep(200);
      }
   }
}

ほとんどのコンパイラではatomic、参照された変数が揮発性になるという副作用があります。次を使用してメモリ ビューを明示的に更新することもできますflush

#pragma omp atomic write
flag[tid] = 1;
#pragma omp flush(flag)

readand句は、write最近の OpenMP バージョンでのみサポートされています。これは、Win32 API を使用しているように見えます。そのため、OpenMP 2.0 のみを実装しているため修飾子をサポートしていない MSVC を使用している可能性がありますが、コードはコンパイルされ、意図したとおりに動作するはずですSleep()readwrite

ビジー ループを回避するもう 1 つの方法は、OpenMP ロックを使用することです。ロックの配列を初期化し、コピー スレッドでそれらを取得し、各処理スレッドにロックの取得を待機させます。

omp_lock_t locks[8];
for (int i = 0; i < 8; i++)
   omp_init_lock(&locks[i]);

#pragma omp parallel num_threads(16)
{
    int tid = omp_get_thread_num();

    // Have the first 8 threads acquire the locks
    if (tid < 8)
       omp_set_lock(&locks[tid]);

    #pragma omp barrier

    // Now locks are set and processing can continue

    if (tid < 8)
    {
       copydata(arrayofPtrs[tid]);
       omp_unset_lock(&locks[tid]);
    }
    else
    {
       omp_set_lock(&locks[tid-8]);
       processingdata(arrayofPtrs[tid-8]);
       omp_unset_lock(&locks[tid-8]);
    }
 }

 for (int i = 0; i < 8; i++)
    omp_destroy_lock(&locks[i]);

OpenMP ロックの代わりに POSIX セマフォの Win32 イベントを使用して同じことを実装することもできます。このアプローチの利点は、フラグが設定されるのを待っている間、明示的にループする必要がないことです。むしろ、omp_set_lock()コピー中のスレッドがそのロックを解放するまで、呼び出しはブロックされます。Win32 イベントを使用WaitForSingleObject(hEvent, INFINITE);すると、コピー スレッドがシグナルを送信するのを待つことができます。

于 2013-04-21T07:51:14.377 に答える