2016-12-09 3 views
3

私はのようなコードを持っていると仮定します。私は今Foo f;ような何かを、別のスレッドにオーバーfへのポインタを渡した場合C++コンストラクタメモリ同期

void InitializeComplexClass(ComplexClass* c); 

class Foo { 
public: 
    Foo() { 
    i = 0; 
    InitializeComplexClass(&c); 
    } 
private: 
    ComplexClass c; 
    int i; 
}; 

、私はどんな店がInitializeComplexClass()によって行われていること何の保証を持っていますfにアクセスする他のスレッドを実行しているCPUに表示されますか? iにゼロを書き込んだ店舗はどうですか?クラスにミューテックスを追加し、コンストラクタでライターロックを取得し、メンバーにアクセスするすべてのメソッドで対応するリーダーロックを取得する必要がありますか?

更新:コンストラクタが返されたら、他のスレッドの束にポインタを渡すとします。私は、コードがx86上で動作していると仮定していませんが、メモリの並べ替えを自由に行うことができるPowerPCのようなもので動くことができます。私は本質的に、コンストラクタが復帰したときに、コンパイラがコードにどのようなメモリ障壁を注入しなければならないかということに興味があります。

+1

本当にスレッド間でどのように共有しているかによって異なります。 – NathanOliver

+0

'Foo'が一度初期化され、複数のスレッドで読み込まれる場合、' const'ポインタを他のスレッドに渡すことをお勧めします。 C++は実際には深いまたは推移的な 'const'を持っていませんが、もしあなたが他のスレッドがメンバを変更することができないならば、ロックなしで逃げるでしょう。 –

+0

そして、可視性が整理されたら、あなた自身をまだ保護していなければ、 'ComplexClass'の非原子的な変化に注意してください。 – user4581301

答えて

0

あなたが行う場合:あなたは別のスレッドにインスタンスのポインタを渡している

Foo f; 
// HERE: InitializeComplexClass() and "i" member init are guaranteed to be completed 
passToOtherThread(&f); 
/* From this point, you cannot guarantee the state/members 
    of 'f' since another thread can modify it */ 

場合は、両方のスレッドが同じインスタンスと対話するために警備員を実装する必要があります。他のスレッドでインスタンスを使用する予定がある場合は、ガードを実装する必要はありません。

passToOtherThread(new Foo()); 

そして、あなたはそれで行われたときにそれを削除してください:しかし、このような新しいインスタンスを渡す、あなたの例のように、スタックポインタを渡すことはありません。

1

あなたの新しいオブジェクトについて他のスレッドが知ることができるようにするには、何らかの理由でそのオブジェクト/信号を渡す必要があります。スレッドに信号を送るために、あなたはメモリに書き込みます。 x86とx64の両方がすべてのメモリ書き込みを順番に実行しますが、CPUはこれらの操作を互いに並べ替えません。これは"Total Store Ordering"と呼ばれ、CPU書き込みキューは「先入れ先出し」のように機能します。
最初にオブジェクトを作成し、を次にを別のスレッドに渡すと、これらのメモリデータの変更も同じ順序で発生し、他のスレッドは常に同じ順序でそれらを表示します。他のスレッドが新しいオブジェクトについて学習するまでには、このオブジェクトの内容は、そのスレッドが以前に(スレッドが何らかの形で見た目を知っていただけであっても)そのスレッドに対して利用可能であることが保証されていました。
結論として、今回は何も同期する必要はありません。が初期化された後にオブジェクトを渡すと、必要なすべての同期が行われます。

更新: TSO以外のアーキテクチャでは、TSOの保証はありません。だからあなたは同期する必要があります。 MemoryBarrier()マクロ(またはインターロックされた操作)、またはいくつかの同期APIを使用します。対応するAPIによって他のスレッドにシグナリングすることも同期を引き起こし、そうでなければ同期APIにはならない。


x86およびx64 CPUは、過去の書き込みを書き直すことがありますが、ここでは関係ありません。より良い理解のために、メモリへの書き込みが書き込みキューを通過し、そのキューをフラッシュするのに時間がかかるので、書き込み後に書き込みを行うことができます。一方、読み取りキャッシュは、常に(独自の書き込みキューを通過した)他のプロセッサーからの最新の更新と一貫しています。

このトピックでは、非常に多くのためにとても信じられないほど混乱行われているが、最終的にx86ベースのx64プログラマは心配する必要があり、物事の唯一のカップルがあります:
は - まず、書き込みの存在でありますキュー(そして、読んでいるキャッシュが気になるはずはありません!)。
- 第2に、データの破損を引き起こす可能性のある非可変長の場合に、異なるスレッドで同じ変数に同時に読み書きすることで、同期メカニズムが必要になります。
- 最後に、複数のスレッドからの同じ変数への同時更新(連動した操作または同期メカニズムがあります)。

+0

私はTSOとx86をよく知っていますが、この特定のコードはアーキテクチャに依存せず、C++メモリモデルによって保証されるものにのみ依存する必要があります。 – eof

+0

@eof一部のアーキテクチャでは、店舗の後に店舗を並べ替えます。したがって、同期が必要です。新しいオブジェクトのポインタを他のスレッドに送るためにメモリに書き込むだけであれば、それは十分ではありません。同期する必要があります。同期化は、実際にはいくつかの連動した操作から成り立ち、常に書き込みキューのフラッシュも発生します。 'MemoryBarrier()'マクロはそれを実装しています。一方、いくつかの同期関数またはAPIを使用して他のスレッドに信号を送る場合、そのAPIはすでにすべてのメモリ同期を行います。 –