11

このサイトにはvolatile、アトミック/マルチスレッド アクセスに変数を使用できるかどうかを尋ねる質問がいくつかあります。たとえば、ここここまたはここを参照してください。

さて、C(++) 標準準拠の答えは明らかにnoです。

ただし、Windows および Visual C++ コンパイラでは、状況はそれほど明確ではないようです。

私は最近、 MSDNの公式ドキュメント回答して引用しましたvolatile

マイクロソフト固有

volatile として宣言されたオブジェクトは (...)

  • 揮発性オブジェクトへの書き込み (揮発性書き込み) にはリリース セマンティクスがあります。グローバルまたは静的オブジェクトへの参照? 命令シーケンスで揮発性オブジェクトへの書き込みが発生する前に、コンパイルされたバイナリで揮発性書き込みが発生する前に発生します。
  • 揮発性オブジェクトの読み取り (揮発性読み取り) には、取得セマンティクスがあります。グローバルまたは静的オブジェクトへの参照 ? これは、命令シーケンスで揮発性メモリの読み取り後に発生するものであり、コンパイルされたバイナリでの揮発性読み取りの後に発生します。

これにより、揮発性オブジェクトをマルチスレッド アプリケーションでのメモリのロックと解放に使用できます。

[鉱山を強調]

さて、これを読むと、揮発性変数はstd::atomic、今後の C++11 標準と同様に、MS コンパイラによって処理されるように思われます。

しかし、私の回答へのコメントで、ユーザーHans Passantは次のように書いています。


注: MSDN に示されているはかなり怪しいように見えます。通常、atomic exchangeなしではロックを実装できないためです。(アレックスも指摘したように。)これはまだ疑問を残しています。この MSDN の記事に記載されている他の情報の有効性、特にhereおよびhereのようなユース ケースの妥当性に注意してください。)


さらに、The Interlocked* 関数のドキュメントがあり、特にvolatile(!?)InterlockedExchange変数を取り、アトミックな読み取りと書き込みを行います。(SO に関する 1 つの質問 -- InterlockedExchange をいつ使用する必要がありますか? -- は、この機能が読み取り専用または書き込み専用のアトミック アクセスに必要かどうかについて正式に回答していないことに注意してください。)

さらに、volatile上記のドキュメントは「グローバルまたは静的オブジェクト」を何らかの形でほのめかしており、「実際の」取得/解放セマンティクスがすべての値に適用されるべきだと私は考えていました。

質問に戻る

Windows では、Visual C++ (2005 - 2010) を使用して、(32bit? int?) 変数を宣言するvolatileと、この変数へのアトミックな読み取りと書き込みが許可されますか?

私にとって特に重要なことは、プログラムが実行されるプロセッサやプラットフォームとは関係なく、これが Windows/ VC ++ で保持される (または保持されない) ことです。(つまり、それが WinXP/32bit であるか、Itanum2 で実行されている Windows 2008R2/64bit であるかは重要ですか?)

検証可能な情報、リンク、テストケースで回答をバックアップしてください!

4

5 に答える 5

6

はい、それらは windows/vc++ でアトミックです (アライメント要件などまたはコースを満たしていると仮定します)

ただし、ロックの場合は、アトミックな更新や読み取りだけでなく、アトミックなテストと設定、または比較と交換の命令などが必要になります。

そうしないと、ロックをテストして、1 つの不可分な操作でロックを要求する方法がありません。

編集: 以下にコメントされているように、32 ビット以下の x86 でのすべてのアラインされたメモリ アクセスはとにかくアトミックです。重要な点は、volatile によってメモリ アクセスが順序付けられることです。(コメントでこれを指摘してくれてありがとう)

于 2011-08-10T07:50:03.360 に答える
3

Visual C++ 2005 以降、volatile 変数はアトミックです。ただし、これは、この特定のクラスのコンパイラと x86/AMD64 プラットフォームにのみ適用されます。たとえば、PowerPC はメモリの読み取り/書き込みの順序を変更する可能性があり、読み取り/書き込みバリアが必要になります。gcc クラスのコンパイラのセマンティクスがどのようなものかはよくわかりませんが、いずれにせよ、アトミックに volatile を使用することはあまり移植性がありません。

参照、最初のコメント「Microsoft 固有」を参照してください: http://msdn.microsoft.com/en-us/library/12a04hfd%28VS.80%29.aspx

于 2011-08-10T07:56:13.577 に答える
1

ちょっと話が逸れますが、とりあえず行ってみましょう。

... The Interlocked* 関数、特に volatile(!) 変数を取る InterlockedExchange のドキュメントがあります ...

これについて考える場合:

void foo(int volatile*);

それは言いますか:

  • 引数はvolatile int へのポインターである必要があります。または
  • 引数はvolatile int へのポインタでもよいですか?

関数は揮発性および非揮発性 int への両方のポインターを渡すことができるため、後者が正しい答えです。

InterlockedExchangeX()したがって、引数が volatile 修飾されているという事実は、それが volatile 整数のみを操作しなければならないことを意味するものではありません。

于 2011-08-10T08:30:57.817 に答える
1

x86 では、これらの操作は、次のような LOCK ベースの命令を必要とせずにアトミックであることが保証されていますInterlocked*(インテルの開発者マニュアル 3A セクション 8.1 を参照)。

基本的なメモリ操作は常にアトミックに実行されます。

• バイトの読み取りまたは書き込み

• 16 ビット境界に整列されたワードの読み取りまたは書き込み

• 32 ビット境界に整列されたダブルワードの読み取りまたは書き込み

Pentium プロセッサ (およびそれ以降の新しいプロセッサ) では、次の追加のメモリ操作が常にアトミックに実行されることが保証されています。

• 64 ビット境界に整列されたクワッドワードの読み取りまたは書き込み

• 32 ビット データ バス内に収まるキャッシュされていないメモリ ロケーションへの 16 ビット アクセス

P6 ファミリ プロセッサ (およびそれ以降の新しいプロセッサ) では、次の追加のメモリ操作が常にアトミックに実行されることが保証されています。

• キャッシュ ライン内に収まるキャッシュ メモリへのアライメントされていない 16、32、および 64 ビット アクセス

これはvolatile、コンパイラによるキャッシュと命令の並べ替えを防ぐためにのみ機能することを意味します (MSVC は揮発性変数のアトミック操作を発行しません。明示的に使用する必要があります)。

于 2011-08-10T08:55:49.043 に答える
1

ポイントは、おそらく次のようなものを許可することです

singleton& get_instance()
{
    static volatile singleton* instance;
    static mutex instance_mutex;

    if (!instance)
    {
        raii_lock lock(instance_mutex);

        if (!instance) instance = new singleton;
    }

    return *instance;
}

instance初期化が完了する前に書き込まれると壊れます。MSVC セマンティクスを使用すると、 が表示されるとすぐにオブジェクトの初期化が完了することが保証されinstance != 0ます (これは、従来の揮発性セマンティクスを使用しても、適切なバリア セマンティクスがなければ当てはまりません)。

このダブルチェック ロック (アンチ) パターンは、実際には非常に一般的であり、バリア セマンティクスを提供しないと機能しなくなります。volatileただし、変数へのアクセスが取得 + 解放バリアであることが保証されている場合は、機能します。

ただし、そのようなカスタム セマンティクスに依存しないでくださいvolatile。これは、既存のコードベースを壊さないようにするために導入されたものだと思います。とにかく、MSDN の例に従ってロックを記述しないでください。おそらく機能しません(バリアだけを使用してロックを作成できるとは思えません。そのためには、アトミック操作(CAS、TASなど)が必要です)。

ダブルチェック ロック パターンを記述する唯一の移植可能な方法は、適切なメモリ モデルと明示的なバリアを提供する C++0x を使用することです。

于 2011-08-10T08:47:28.237 に答える