web-dev-qa-db-ja.com

空のボディをチェックしながら揮発性整数をループしながら-これはどういう意味ですか?

次の行を含むC++クラスを探しています。

while( x > y );
return x - y;

xおよびyは、タイプvolatile intのメンバー変数です。私はこの構造を理解していません。

私はここにコードスタブを見つけました: https://Gist.github.com/r-lyeh/cc50bbed16759a99a226 。私はそれが正しいか、またはうまくいくことさえ保証されないと思います。

34
Luca

xyは揮発性として宣言されているため、プログラマーはそれらがプログラムの外部から変更されることを期待しています。

その場合、コードはループに残ります

 while(x>y);

そして、値がx-yのように外部から変更された後、値x <= yを返します。これが書かれている正確な理由は、コードとどこで見たのかを詳しく教えてから推測できます。この場合のwhileループは、他のイベントが発生するのを待機する手法です。

62
therainmaker

そうみたいです

while( x > y );

スピニングループ です。 x <= yまで停止しません。 xyvolatileであるため、このルーチンの外で変更できます。したがって、x <= yがtrueになると、x - yが返されます。この手法は、イベントを待機するために使用されます。

更新

あなたが追加した the Gist によると、アイデアはスレッドセーフなロックフリーの循環バッファーを実装することだったようです。はい、実装は正しくありません。たとえば、元のコードスニペットは

unsigned count() const {
    while( tail > head );
    return head - tail;
}

tailhead以下になったとしても、head - tailが正の数を返すことは保証されていません。スケジューラは、whileループの直後に別のスレッドに実行を切り替え、そのスレッドはhead値を変更する場合があります。とにかく、共有メモリへの読み取りと書き込みの仕組み(メモリの並べ替えなど)に関連する他の多くの問題があるため、このコードは無視してください。

25
Stas

y(またはリンクされた例ではhead)がvolatile変更として宣言されているため、他の応答は命令の機能をすでに詳細に指摘しています。別のスレッドからその変数に変更すると、条件が満たされたときにwhileループが終了します。

ただし、 リンクされたコードの例 は非常に短くても、[〜#〜]ではない[〜#〜]コードを記述します。

まず最初に

_while( tail > head );
_

大量のCPUサイクルを浪費し、条件が満たされるまで1つのコアをロックします。

コードは進むにつれてさらに良くなります。

_buffer[head++ % N] = item;
_

ここでは、プリインクリメント付きのポストミスと指摘したJABに感謝します。影響を修正しました。locksまたはmutexesがないため、明らかに最悪の状態を想定する必要があります。 itemに値を割り当てた後、_head++_が実行される前にスレッドが切り替わります。その後、マーフィーはこのステートメントを含む関数を再度呼び出し、同じitem位置にheadの値を割り当てます。その後、headが増加します。次に、最初のスレッドに戻り、headを再度インクリメントします。だから代わりに

_buffer[original_value_of_head+1] = item_from_thread1; 
buffer[original_value_of_head+2] = item_from_thread2;
_

私たちは結局

_buffer[original_value_of_head+1] = item_from_thread2; 
buffer[original_value_of_head+2] = whatever_was_there_previously;
_

スレッド数が少ないクライアント側では、このようなずさんなコーディングを回避できますが、サーバー側では、これは時限爆弾と見なすことができます。代わりに、locksやmutexesなどの同期構成を使用してください。

そしてまあ、完全を期すために、行

_while( tail > head );
_

メソッドのpop_back()

_while( tail >= head );
_

実際に押し込んだよりも1つ多い要素をポップしたい場合(または、何かを押し込む前に1つの要素をポップしたい場合)を除きます。

基本的に結局長い怒りに要約されるものを書いて申し訳ありませんが、もしこれがその卑猥なコードをコピーして貼り付けることを一人だけに保つなら、それは価値がありました。

更新:while(x>y);のようなコードが実際に完全に理にかなっている例を提供するかもしれません。実際、「古き良き」時代には、このようなコードをかなり頻繁に目にしていました。 DOS。ただし、スレッドのコンテキストでは使用されませんでした。主に、割り込みフックを登録することができなかった場合のフォールバックとして(子供たちはそれを「イベントハンドラーを登録できない」と解釈するかもしれません)。

_startAsynchronousDeviceOperation(..);
_

これは、たとえば、 hardiskにDMAを介してデータを読み取るように指示するか、サウンドカードにDMAを介して録音するように指示します。異なるプロセッサー(GPUなど)の関数を呼び出すこともできます。通常、outb(2)のようなものを介して開始されます。

_while(byteswritten==0); // or while (status!=DONE);
_

デバイスとの唯一の通信チャネルが共有メモリである場合は、そうです。最近はデバイスドライバーやマイクロコントローラー以外でも、このようなコードが表示されることはありません。明らかに、メモリの場所が最後に書き込まれた場所であるとスペックが述べていることを前提としています。

20
Syren Baran

volatileキーワードは、特定の最適化を防ぐように設計されています。この場合、キーワードがないと、コンパイラーはwhileループを具体的な一連の命令に展開して、値が外部から変更される可能性があるため、実際には明らかに壊れます。

以下を想像してみてください:

int i = 2;
while (i-- > 0) printf("%d", i);

ほとんどのコンパイラーはこれを見て、単にprintfへの2つの呼び出しを生成します-volatileキーワードを追加すると、代わりに、2に設定されたカウンターと値のチェックを呼び出すCPU命令が生成されますすべての反復の後。

例えば、

volatile int i = 2;
this_function_runs_on_another_process_and_modifies_the_value(&i);
while(i-- > 0) printf("%d", i);
7
Olipro