2013-04-19 9 views
20

他のスレッドが終了するためのフラグを設定する必要があります。他のスレッドは時折出口フラグをチェックします。私は旗のために原子を使わなければならないのか、それとも普通のブールだけで十分ですか?"exit" bool変数にアトム<bool>を使用する必要がありますか?

#include <future> 
bool exit = false; 
void thread_fn() 
{ 
    while(!exit) 
    { 
     //do stuff 
     if(exit) break; 
     //do stuff 
    } 
} 
int main() 
{ 
    auto f = std::async(std::launch::async, thread_fn); 
    //do stuff 
    exit = true; 
    f.get(); 
} 

答えて

21

私は「出口」のブール変数をアトミック使用する必要がありますか?

はい

atomic<bool>を使用するか、std::mutexなどの手動同期を使用してください。あなたのプログラムは現在データレースを含んでいますが、1つのスレッドが別のスレッドが変数を読み込んでいる可能性があります。これは未定義の動作です。 C++ 11標準の1.10/21段落当たり

:それは異なるスレッド内の2つの競合アクションが含まれている場合、プログラムの実行は、データ・レースが含ま

の 少なくとも一つ原子的ではなく、どちらも他のものの前に起こらない。そのようなデータレースの結果は であり、未定義の動作になります。そのうちの一つは、メモリ位置(1.7)、他方 を変更する場合

2つの発現評価競合

競合」の定義は、パラグラフ1.10/4で与えられます。同じメモリ位置にアクセスまたは変更する。

+0

実際には「ブール」へのアクセスはどのようにアトミックなものになるのかほとんどわかりません(私は正式な標準では同意しますが、それは他のすべての問題です)。とにかく、競合状態とは別に、私は、コンパイラがデータをキャッシュしたり、命令を並べ替えることを保証するためのメモリバリア( 'atomic <>'または 'std :: mutex'によって提供される)も必要と考えています。しかし、私はそれを正しく説明するための理論的知識が欠けています(実際にそれを使用する方法はわかっていますが)できる場合は私に教えてください。それとも新しい質問を作成すべきですか? – syam

+14

標準的なデータ競合状態は、(狭く)精緻化するために、あるスレッドが他のスレッドが見ることができるものだけではありません。また、最適化やコードの並べ替えについても説明します。コンパイラはデータレースを想定できないため、あるスレッドが変数を読み込み、それを変更しないと、同期ポイント間で値を変更することはできません。スレッドは決して終了しません。スレッドがフラグを使用せずにフラグを設定した場合、変更は他のコードの前に来るように並べ替えられます(最後の同期ポイントの後)。私はキャッシュの一貫性を無視しています。これがアトミックが存在する理由です。 –

+0

@Andyポイントまで正確に引用 - 私が原子を使用するのに十分なほど。うまくいけば、彼の答えでピートは、プレーンなブールを使用することで発生する可能性が私の例のコードで実際の問題の説明を明確にします。 – PowerGamer

11

はい、は、に同期が必要です。あなたが言うように、最も簡単な方法はatomic<bool>です。

正式には、@AndyProwlによると、言語定義ではここでアトミックを使用しないと未定義の動作が行われると言われています。それには十分な理由があります。

まず、スレッドスイッチの途中で変数の読み込みまたは書き込みを中断することができます。もう一方のスレッドは部分的に書き込まれた値を参照するか、値を変更した場合は元のスレッドに混合値が表示されます。第2に、2つのスレッドが異なるコア上で動作するとき、それらは別々のキャッシュを有する。値を書き込むとキャッシュに値が格納されますが、他のキャッシュは更新されないため、スレッドは別のスレッドによって書き込まれた値を見ることができません。第三に、コンパイラは、それが見ているものに基づいてコードを再編成することができます。このコード例では、ループ内の何もがexitの値を変更しない場合、コンパイラは値が変更されると思われる理由はありません。ループをwhile(1)にすることができます。

アトミックスは、これら3つの問題すべてに対処します。

+1

答えをもう少し詳しく述べることができますか?最初の問題は、0または1の2つの値(「半分」の値はありません)のみを持つブール変数の問題とはどのようにして正確になりますか? 2番目の問題: "exit" varに書き込んだスレッドがキャッシュに書き込むことができ、キャッシュからの値が "exit" varが存在するメモリに入ることはないと言っていますか?第3の問題:コンパイラは理論的には、a)thread_fn()の "exit"を呼び出す前に常に真であり、b)thread_fn()が "exit"ループを変更する権利、正しい? – PowerGamer

+1

@PowerGamer - はい、ブール変数の引き裂きはありそうもありません。私は仮説的に起こる可能性がある逆シナリオを通らない。他のタイプについては、それを防ぐための措置を取らなければ、それが起こります。 –

-1

実際、この特定の例では、普通のブールでは何も問題になりません。唯一の注意は、bool終了変数をvolatileとして宣言してメモリに保持することです。 CISCとRISCの両方のアーキテクチャは、厳密にアトミックなプロセッサ命令としてboolの読み書きを実装しています。現代のマルチコアプロセッサも高度なスマートキャッシュを実装しています。したがって、あらゆるメモリ障壁は必要ではありません。標準引用はこの唯一のスレッドからの唯一の書込みと読書を取り扱うので、この特定のケースには適切ではない。

+1

x86では、bool型を_define_していないため、アトミックにすることもできません。 – MSalters

関連する問題