34

私はMSDNのロックレスプログラミングに関する記事を読んでいます。それは言う:

最近のすべてのプロセッサでは、自然に整列されたネイティブタイプの読み取りと書き込みはアトミックであると想定できます 。メモリバスが少なくとも読み取りまたは書き込み中のタイプと同じ幅である限り、CPUはこれらのタイプを単一のバストランザクションで読み取りおよび書き込みし、他のスレッドがそれらを半分完了した状態で見ることを不可能にします。

そしてそれはいくつかの例を与えます:

// This write is not atomic because it is not natively aligned.
DWORD* pData = (DWORD*)(pChar + 1);
*pData = 0;

// This is not atomic because it is three separate operations.
++g_globalCounter;

// This write is atomic.
g_alignedGlobal = 0;

// This read is atomic.
DWORD local = g_alignedGlobal;

私はたくさんの回答とコメントを読んで、C ++でアトミックであることが保証されているものはなく、標準でも言及されていないので、SOでは少し混乱しています。私は記事を誤解していますか?それとも、記事の執筆者は、非標準でMSVC ++コンパイラに固有のものについて話しますか?

したがって、記事によると、以下の割り当てはアトミックでなければなりませんよね?

struct Data
{
    char ID;
    char pad1[3];
    short Number;
    char pad2[2];
    char Name[5];
    char pad3[3];
    int Number2;
    double Value;
} DataVal;

DataVal.ID = 0;
DataVal.Number = 1000;
DataVal.Number2 = 0xFFFFFF;
DataVal.Value = 1.2;

それが本当の場合、置換Name[5]と置換はメモリアライメントに違いをもたらしますかpad3[3]std::string Name;変数への割り当てNumber2Value変数は引き続きアトミックですか?

誰か説明してもらえますか?

4

8 に答える 8

30

この推奨事項はアーキテクチャ固有です。これは、x86およびx86_64(低レベルプログラミング)に当てはまります。また、コンパイラがコードを並べ替えないことを確認する必要があります。そのために「コンパイラのメモリバリア」を使用できます。

x86の低レベルのアトミック読み取りおよび書き込みについては、インテルリファレンスマニュアル「インテル®64およびIA-32アーキテクチャーソフトウェア開発者マニュアル」第3A巻(http://www.intel.com/Assets/PDF/manual/253668)で説明されています。 pdf)、セクション8.1.1

8.1.1保証された不可分操作

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

  • バイトの読み取りまたは書き込み
  • 16ビット境界に揃えられた単語の読み取りまたは書き込み
  • 32ビット境界に整列されたダブルワードの読み取りまたは書き込み

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

  • 64ビット境界に整列されたクアッドワードの読み取りまたは書き込み
  • 32ビットデータバス内に収まるキャッシュされていないメモリ位置への16ビットアクセス

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

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

このドキュメントには、Core2などの新しいプロセッサー向けのアトミックな説明も含まれています。すべての非整列操作がアトミックになるわけではありません。

他のインテルのマニュアルでは、このホワイトペーパーを推奨しています。

http://software.intel.com/en-us/articles/developing-multithreaded-applications-a-platform-consistent-approach/

于 2011-02-15T10:09:50.410 に答える
12

あなたは引用を誤解していると思います。

特定の命令(このアーキテクチャに適切)を使用して、特定のアーキテクチャでアトミシティを保証できます。MSDNの記事では、C ++の組み込み型での読み取りと書き込みは、アーキテクチャーではアトミックであると予想されると説明されています。x86

ただし、C ++標準はアーキテクチャが何であるかを想定していないため、標準はそのような保証を行うことはできません。実際、C ++は、ハードウェアサポートがはるかに制限されている組み込みソフトウェアで使用されます。

C ++ 0xはstd::atomicテンプレートクラスを定義します。これにより、タイプに関係なく、読み取りと書き込みをアトミック操作に変換できます。コンパイラーは、標準に準拠した方法で対象となる型の特性とアーキテクチャーに基づいて、アトミック性を取得するための最良の方法を選択します。

InterlockExchange新しい標準では、ハードウェアによって提供される最速の(ただし安全な)利用可能なプリミティブにコンパイルされるMSVCと同様の多くの操作も定義されています。

于 2011-02-15T10:47:22.427 に答える
3

C ++標準は、アトミックな動作を保証するものではありません。ただし、実際には、記事に記載されているように、単純なロードおよびストア操作はアトミックになります。

アトミック性が必要な場合は、それについて明示し、何らかのロックを使用することをお勧めします。

*counter = 0; // this is atomic on most platforms
*counter++;   // this is NOT atomic on most platforms
于 2011-02-15T09:55:22.333 に答える
2

単純なワードサイズ操作のアトミック性に依存する場合は、予想とは異なる動作をする可能性があるため、十分に注意してください。マルチコアアーキテクチャでは、読み取りと書き込みの順序が乱れることがあります。これを防ぐには、メモリバリアが必要になります。(詳細はこちら)。

アプリケーション開発者にとっての結論は、OSがアトミックであることを保証するプリミティブを使用するか、適切なロックを使用することです。

于 2011-02-15T10:00:26.950 に答える
1

彼らが乗り越えようとしているのは、ハードウェアによってネイティブに実装されたデータ型がハードウェア内で更新されるため、別のスレッドからの読み取りで「部分的に」更新された値が得られないことだと思います。

32ビット以上のマシンで32ビット整数を考えてみましょう。1命令サイクルで完全に書き込まれるか読み取られますが、より大きなサイズのデータ​​型、たとえば32ビットマシンの64ビット整数はより多くのサイクルを必要とするため、理論的には、それらのサイクルの間にスレッドの書き込みが中断される可能性があります。有効な状態ではありません。

文字列は高レベルの構造であり、ハードウェアに実装されていないため、文字列を使用してもアトミックにはなりません。編集:文字列への変更についてあなた(didnt)が何を意味するかについてのあなたのコメントによると、別の回答で述べられているように、コンパイラはデフォルトでフィールドを整列します。

それが標準にない理由は、記事で述べられているように、これは最新のプロセッサがどのように命令を実装するかについてであるということです。標準のC/C ++コードは、16ビットまたは64ビットのマシンでもまったく同じように機能するはずです(パフォーマンスの違いがあります)が、64ビットのマシンでのみ実行すると仮定すると、64ビット以下のものはすべてアトミックです。(SSEなどのタイプは別として)

于 2011-02-15T10:00:34.820 に答える
1

IMOの記事には、基盤となるアーキテクチャに関するいくつかの前提条件が組み込まれています。C ++にはアーキテクチャに関する最小限の要件しかないため、たとえばアトミック性についての保証は標準では提供できません。たとえば、バイトは少なくとも8ビットである必要がありますが、バイトが9ビットであるアーキテクチャを使用できますが、理論的にはint16...です。

したがって、コンパイラーがx86アーキテクチャーに固有である場合、特定の機能を使用できます。

注意:構造体は通常、デフォルトでネイティブの単語境界に揃えられます。#pragmaステートメントでこれを無効にできるため、パディングフィルは必要ありません

于 2011-02-15T09:57:10.930 に答える
1

atomicity記事で言及されているように、実用性はほとんどないと思います。これは、有効な値を読み書きできますが、おそらく古くなっていることを意味します。したがって、intを読み取ると、古い値から2バイト、別のスレッドによって現在書き込まれている新しい値から他の2バイトではなく、完全に読み取られます。

共有メモリにとって重要なのは、メモリバリアです。また、C++0xatomicタイプmutexesなどの同期プリミティブによって保証されています。

于 2011-02-15T11:48:48.577 に答える
0

インデックス演算子は基になる文字への直接参照を返すため、個々の文字の割り当てにのみ使用している場合はchar Name[5]、に変更してstd::string Nameも違いはないと思います。完全な文字列代入はアトミックではありません(char配列ではできないので、とにかくこのように使用することを考えていなかったと思います)。

于 2011-02-15T10:02:59.267 に答える