18

std::threadaが実行を終了したかどうかを確認したいと思います。stackoverflow を検索すると、この問題に対処する次の質問が見つかりました。受け入れられた答えは、終了する直前にワーカースレッドに変数を設定させ、メインスレッドにこの変数をチェックさせることを提案しています。このようなソリューションの最小限の実例を次に示します。

#include <unistd.h>
#include <thread>

void work( bool* signal_finished ) {
  sleep( 5 );
  *signal_finished = true;
}

int main()
{
  bool thread_finished = false;
  std::thread worker(work, &thread_finished);

  while ( !thread_finished ) {
    // do some own work until the thread has finished ...
  }

  worker.join();
}

受け入れられた回答についてコメントした人は、単純なbool変数をシグナルとして使用することはできず、コードはメモリバリアなしで壊れており、使用std::atomic<bool>は正しいと主張しています。私の最初の推測では、これは間違っていて単純boolで十分ですが、何かが欠けていないことを確認したいと思います。上記のコードstd::atomic<bool>を正しくするには、 が必要ですか?

メインスレッドとワーカーが異なるソケットの異なる CPU で実行されていると仮定しましょう。私が思うに、メイン スレッドthread_finishedは CPU のキャッシュから読み取ります。ワーカーがそれを更新すると、キャッシュ コヒーレンシ プロトコルがワーカーの変更をグローバル メモリに書き込み、メイン スレッドの CPU のキャッシュを無効にする処理を行うため、グローバル メモリから更新された値を読み取る必要があります。上記のようなコードを機能させるには、キャッシュの一貫性が重要なのではないでしょうか?

4

4 に答える 4

23

受け入れられた回答についてコメントした人は、単純な bool 変数をシグナルとして使用することはできず、コードはメモリバリアなしで壊れており、 std::atomic を使用することは正しいと主張しています。

コメンターは正しいです:に設定されboolているスレッドからの非アトミックな書き込みは並べ替えられる可能性があるため、シンプルでは不十分です。thread_finishedtrue

次のように、静的変数xを非常に重要な数値に設定し、その終了を通知するスレッドを考えてみましょう。

x = 42;
thread_finished = true;

メイン スレッドがthread_finishedに設定されtrueていることを確認すると、ワーカー スレッドが終了したと見なされます。ただし、メイン スレッドが を調べるxと、上記の 2 つの書き込みの順序が変更されているため、間違った番号に設定されていることがわかる場合があります。

もちろん、これは一般的な問題を説明するための単純化された例にすぎません。変数にを使用std::atomicすると、メモリ バリアthread_finishedが追加され、すべての書き込みが完了する前に確実に行われます。これにより、順不同の書き込みの潜在的な問題が修正されます。

もう 1 つの問題は、不揮発性変数への読み取りが最適化される可能性があることです。であるため、メイン スレッドはthread_finishedフラグの変更に気付くことはありません。


重要な注意: thread_finishedvolatileにしても問題は解決しません。実際、volatile は、スレッド化と組み合わせて使用​​すべきではありません。これは、メモリ マップされたハードウェアでの作業を目的としています。

于 2013-01-16T19:04:05.667 に答える
7

raw を使用するboolだけでは不十分です。

プログラムの実行にデータ競合が含まれているのは、異なるスレッドに競合する 2 つのアクションが含まれており、そのうちの少なくとも 1 つがアトミックではなく、どちらも他のスレッドの前に発生しない場合です。このようなデータ競合は、未定義の動作を引き起こします。§ 1.10 p21

2 つの式の評価は、一方がメモリ ロケーション (1.7) を変更し、もう一方が同じメモリ ロケーションにアクセスまたは変更する場合に競合します。§ 1.10 p4

プログラムには、ワーカー スレッドが bool に書き込み、メイン スレッドが bool から読み取るデータ競合が含まれていますが、操作間に正式な事前発生関係はありません。

std::atomic<bool>データ競合を回避するには、適切なメモリ順序で使用する、メモリ バリアを使用する、bool を条件変数に置き換えるなど、さまざまな方法があります。

于 2013-01-16T21:43:04.583 に答える
3

大丈夫じゃない。オプティマイザーは最適化できます

  while ( !thread_finished ) {
    // do some own work until the thread has finished ...
  }

に:

  if(!thread_finished)
    while (1) {
      // do some own work until the thread has finished ...
    }

それが証明できれば、「ある自分の仕事」は変わらないthread_finished

于 2013-01-16T19:03:44.010 に答える
2

キャッシュ コヒーレンシ アルゴリズムはどこにでも存在するわけではなく、完璧でもありません。関連する問題thread_finishedは、あるスレッドが値を書き込もうとしているときに、別のスレッドが値を読み込もうとしているということです。これはデータ競合であり、アクセスが順序付けされていない場合、未定義の動作が発生します。

于 2013-01-16T19:04:16.330 に答える