簡単な質問ですか?この行は C++ と Java でアトミックですか?
class foo {
bool test() {
// Is this line atomic?
return a==1 ? 1 : 0;
}
int a;
}
その行に複数のスレッドがアクセスしている場合、最初に a==1 のチェックを実行してから、a を更新してから戻ることになる可能性がありますよね?
追加:私はクラスを完了していません。もちろん、更新する他の部分があります...
いいえ、C++ と Java の両方に対応しています。
Java では、同じ方法でメソッドを作成し、synchronized他の用途を保護する必要があります。aすべての場合において、同じオブジェクトで同期していることを確認してください。
C++ では、を使用std::mutexして保護する必要がありますa。おそらくstd::lock_guard、関数の最後でミューテックスを適切にロック解除するために使用します。
return a==1 ? 1 : 0;
簡単な書き方です
if(a == 1)
return 1;
else
return 0;
a を更新するためのコードが表示されません。しかし、私はあなたがそれを理解できると思います。
書き込みがあるかどうかに関係なく、C++ での非アトミック型の値の読み取りはアトミック操作ではありません。書き込みがない場合、それがアトミックかどうかは気にしないかもしれません。他のスレッドが値を変更している可能性がある場合は、確かに気にします。
正しい言い方は簡単です:いいえ!(Java と C++ の両方)
あまり正確ではありませんが、より実用的な答えは次のとおりです。技術的にはこれはアトミックではありませんが、ほとんどの主流のアーキテクチャでは、少なくとも C++ の場合です。
投稿したコードでは何も変更されていません。変数はテストされているだけです。したがって、コードは通常、TESTそのメモリ位置にアクセスする単一の (または同様の) 命令、つまりアトミックになります。命令はキャッシュラインを読み取り、それが何であれ、それぞれのロケーションに明確に定義された値が1つあります。
ただし、これは偶発的/偶発的なものであり、信頼できるものではありません。
通常、他の単一のスレッドが値に書き込む場合でも、偶発的/偶然に機能します。このために、CPU はキャッシュ ラインをフェッチし、キャッシュ ライン内のそれぞれのアドレスの位置を上書きし、キャッシュ ライン全体を RAM に書き戻します。変数をテストするときは、古い値または新しい値 (間に何もない) を含むキャッシュ ラインをフェッチします。あらゆる種類の事前発生保証はありませんが、これを「アトミック」と見なすことはできます。
複数のスレッドがその変数を同時に変更する場合は、はるかに複雑になります (問題の一部ではありません)。これが適切に機能するためには、 C++11<atomic>の何かを使用するか、アトミック組み込み関数などを使用する必要があります。そうしないと、何が起こり、操作の結果がどうなるかが非常に不明確になります。あるスレッドが値を読み取り、インクリメントして書き戻す可能性がありますが、別のスレッドは、変更された値が書き戻される前に元の値を読み取る可能性があります。
これは、現在のすべてのプラットフォームで、多かれ少なかれ悪い結果になることが保証されています。
いいえ、それは(一般的に)アトミックではありませんが、一部のアーキテクチャーでは可能です(たとえば、C ++では、整数が整列されている場合はIntelで、強制されない限り、整列されます)。
次の3つのスレッドについて考えてみます。
// thread one: // thread two: //thread three
while (true) while (true) while (a) ;
a = 0xFFFF0000; a = 0x0000FFFF;
書き込み先aがアトミックでない場合(たとえば、Intelaがアラインされていない場合、および2つの連続するキャッシュラインのそれぞれで16ビットと議論するため)。3番目のスレッドはループから抜け出せないように見えますが(の2つの可能な値aは両方ともゼロ以外です)、実際には割り当てはアトミックではなく、スレッド2は上位16ビットを0に更新できます。スレッド3は、スレッド2が更新を完了する時間を取得してループから抜け出す前に、下位16ビットを0として読み取ることができます。
戻り値はスレッドに対してローカルであるため、条件全体は質問とは無関係です。
あなたの質問は次のように言い換えることができます: is statement:
a == 1
アトミックかどうか?いいえ、アトミックではありません。 std::atomic を使用するか、何らかのロック下でその条件を確認する必要があります。三項演算子全体がアトミックかどうかは、何も変更しないため、このコンテキストでは重要ではありません。このコードの場合、質問で意味する場合:
bool flag = somefoo.test();
== 1と一致するようにフラグを立ててください。それは間違いなく、質問の三項演算子全体がアトミックである場合は無関係です。
次のコードを検討してください。
bool done = false;
void Thread1() {
while (!done) {
do_something_useful_in_a_loop_1();
}
do_thread1_cleanup();
}
void Thread2() {
do_something_useful_2();
done = true;
do_thread2_cleanup();
}
これら 2 つのスレッド間の同期は、ブール変数 done を使用して行われます。これは、2 つのスレッドを同期する間違った方法です。
x86 では、最大の問題はコンパイル時の最適化です。
do_something_useful_2() のコードの一部は、コンパイラによって「done = true」の下に移動できます。do_thread2_cleanup() のコードの一部は、コンパイラによって "done = true" の上に移動できます。do_something_useful_in_a_loop_1() が「done」を変更しない場合、コンパイラは Thread1 を次のように書き換える可能性があります。
if (!done) {
while(true) {
do_something_useful_in_a_loop_1();
}
}
do_thread1_cleanup();
そのため、Thread1 は終了しません。
x86 以外のアーキテクチャでは、キャッシュ効果または順不同の命令実行により、他の微妙な問題が発生する可能性があります。
ほとんどの人種検出器は、そのような人種を検出します。
また、ほとんどの動的競合検出器は、この bool と同期することを意図したメモリ アクセスでのデータ競合を報告します。
(つまり、do_something_useful_2() と do_thread1_cleanup() の間)
このような競合を修正するには、コンパイラやメモリ バリアを使用する必要があります (専門家でない場合は、単にロックを使用してください)。
ここには多くの良い答えがありますが、Java でaとしてマークする必要性について言及しているものはありませんvolatile。
これは、他の同期方法が採用されていない場合に特にa重要ですが、他のスレッドが更新する可能性があります。そうしないと、 の古い値を読み取っている可能性がありますa。
いいえ、それはまだテストであり、セットとリターンが続きます。
はい、マルチスレッド化が問題になります。
それは単なる構文糖です。