2017-12-21 28 views
2

スレッド間でデータ構造体を共有したい(gcc、Linux、x86)。 のは、私は、スレッドAに次のコードを持っているとしましょう:x86上でgccを使用してメモリを注文する方法

shared_struct->a = 1; 
shared_struct->b = 1; 
shared_struct->enable = true; 

スレッドBはenableフラグの構造体最初のことをチェックし、定期的な作業です。

私は、コンパイラがスレッドAの書き込みを並べ替えることができるので、スレッドBは矛盾したデータを見ることができると思います。私はARMのメモリ障壁に精通していますが、私はx86で書き込み順序を保証するにはどうすればよいですか?volatileより良い方法はありますか?

構造体に一貫性のある状態を設定し、すべてをメモリにフラッシュし、最後に有効フラグを設定したいだけです。

+5

これはまったくありません。アトミックフラグがある場合は、C11 ' ' –

+0

を使用してください。これは、揮発性のアクセスが並べ替えられない「副産物」です。 – filo

+0

@AnttiHaapalaアトミックアクセスは命令の並べ替えと何が関係していますか? – Lundin

答えて

3

あなたが本当に(あなたがのPthreadを言及するので)ミューテックスを使用するので、shared_structpthread_mutex_lock mtx;フィールドを追加する必要があり、他には、その後と似

pthread_mutex_lock(&shared_struct->mtx); 
shared_struct->a = 1; 
shared_struct->b = 1; 
shared_struct->enable = true; 
pthread_mutex_unlock(&shared_struct->mtx); 

とを(pthread_mutex_initで初期化することを忘れないでください)共有データにアクセスするコード。

また、atomic operationsを調べることもできます(ただし、あなたの場合は、上記のようにmutexを使用することをお勧めします)。

pthread tutorialをお読みください。

race conditionsおよびundefined behaviorを避けてください。

どのように私はあなたがスレッドライブラリを実装している場合を除きは、(それの一部アセンブラでコーディングし、futex(7)を使用しなければならない)ことを行いません

を注文する書き込みを確保しますGNU glibc(またはmusl-libc)のnptl(7)の実装はpthreads(7)です。ミューテックスを使用すべきであり、スレッドライブラリを実装する時間を無駄にしたくないので(既存のスレッドライブラリを使用する) (glibcの& MUSL-libcを含む)、Linux上で最もC標準ライブラリはfree softwareなので、(あなたがのpthreadミューテックスが実装されているかを理解するために興味がある場合など、)あなたは自分のソースコードを学ぶことができ

は注意してください。

コンパイラはそれがほとんどない書き込み

(と確かにだけではなく)コンパイラが、ハードウェアを並べ替えることができます。 cache coherenceについて読むまた、OSも関与している可能性があります(futex(2) pthread mutexルーチンによって呼び出されることもあります)。

+0

ありがとう - 私は明白なミューテックスについて忘れました。 – filo

+1

補足として、 'volatile'_does_はハードウェアの再順序付けを防ぎます。ハードウェアもコンパイラもC仕様から逸脱することは許されません。揮発性変数がプログラマによって指定された通りに順序付けされないように、ハードウェアを再命令するシステムは、適合していない。ハードウェアとコンパイラのどちらも、CプログラムがCの標準の「抽象マシン」の必要に応じて実行されることを保証できない場合、Cで書かれたプログラムはそのシステムで使用できません。プログラマにメモリバリアを扱う負担をかけることは、システムを適合させることにはならない。 – Lundin

+0

ここでも 'pthread_rwlock'が適切です.IMOはユースケースをよく適合させ、複数のリーダースレッドが互いに干渉しないようにします。 –

2

enable = trueを設定する必要がある場合は、リリース/取得オーダーを使用してstdatomic.hを入力してください。 (x86 asmでは、通常のストア/ロードにはセマンティクスがリリース/取得されているので、コンパイル時の並べ替えをブロックするだけで十分ですが、ではなくatomicを使用するのが正しい方法です。)

しかし、あなたは、あなたがそれを変更しながら、再び「ロックアウト」の読者にenable = falseを設定することができるようにしたい場合は、より複雑な更新パターンが必要です。アトミックで手動でミューテックスを再現するか(悪いアイデア;それの代わりに標準ライブラリミューテックスを使用する)、更新の途中でライターがいないときに複数のリーダーによる待機なしの読み取り専用アクセスを可能にするものを実行します。

RCUaまたはseqlockのいずれかが良い場合はとなります。

seqlockの場合、enable = true/falseフラグの代わりに、シーケンス番号があります。リーダは、他のメンバーを読み取った後にシーケンス番号を確認し、その後にもう一度チェックすることによって、「破れた」書き込みを検出することができる。 (しかし、すべてのメンバーは少なくともmo_relaxedを使用してatomicでなければなりません。それ以外の場合は、値を破棄してもCで読み取られたデータの競合が未定義のデータ競合となります。おそらく最初のもので取得してから、shared_struct->bの負荷で取得して、シーケンス番号の2番目の負荷がそれ以降に注文されることを確認してください(acquireは一方通行の障壁に過ぎません。あなたが必要とするもの)

RCUは、読者が常に完全に待ち時間をなくし、現在有効な構造体へのポインタを逆参照するだけです。あなたはすべてのリーダースレッドがmemブロックを読み終えていることを確認しなければなりませんあなたがそれを再使用する前にオーディー。


は単に作家がそれらを変更している間に、他のメンバーのために一貫性のない/部分的に更新された値を見て、その後enable == trueを見てからリーダーを停止していない他の構造体のメンバーを変更し、前enable = falseを設定します。これを行う必要はなく、他のスレッドがアクセスするために新しいオブジェクトを解放するだけの場合は、記述するシーケンスはatomic_store_explicit(&foo->enable, true, memory_order_release)で問題ありません。

+0

'memory_order_release'ストアは読者のenableフラグの' memory_order_acquire'ロードと対になるでしょうか? – caf

+0

@caf:はい。操作(およびseq_cst)操作を「同期化」のrelease(およびseq_cst)操作を取得します。 http://preshing.com/20120913/acquire-and-release-semantics/には、これが実際のマシン上でどのように実際に動作するのか、そしてそれが何をしているのかということについて素晴らしい情報があります。 –

関連する問題