2012-03-21 28 views
11

は、次のように私は、スレッドによる使用のためのグローバル変数を初期化するプログラムがあると、新しいスレッドに読み取り専用のデータを渡します遭遇するかもしれない。私はこのソリューションを移植性のあるものにしたいと考えています。これは、x86などのメモリ保証が強化されたアーキテクチャに特有のものではありません。は安全

  1. 一般的な質問:非常に一般的ですが、これはすべてのプロセッサアーキテクチャで本当に安全ですか?それをどうやって安全にするか?
  2. グローバル変数はvolatileではありません。 StartThread()コールの後で並べ替えられ、私を縛られたままにする可能性はありますか?この問題を解決するには?
  3. コンピュータに、独自のキャッシュを持つ2つのプロセッサがあるとします。メインスレッドは第1のプロセッサ上で実行され、ワー​​カースレッドは第2のプロセッサ上で実行される。プログラムがSomeFunction()の実行を開始する前に、ThreadParameterを含むメモリブロックが各プロセッサのキャッシュにページされたとします。 SomeFunction()5ThreadParameterと書き込むと、最初のプロセッサのキャッシュに格納され、次に2番目のプロセッサで実行されるワーカースレッドが開始されます。 2番目のプロセッサのメモリページで、最初のプロセッサからの更新がまだ見られていないため、2番目のプロセッサのWorkerThread()には5の期待値ではなく、ThreadParameterの初期化されていないデータが表示されますか?
  4. 単純なintではなく、これを最もうまく処理するには、マルチスレッド環境で必ずしも使用されない複雑なデータ型へのポインタを使用することができますか?

私の心配が根拠がない場合は、私が心配する必要がない具体的な理由は何ですか?

+0

SomeFunctionが複数のWorkerThreadを呼び出すと仮定します。私はあなたの質問がもっと似ていると思います: "同時読みで私に問題が起きますか?" ....? – Alex

+0

まあ、POSIXスレッドを使うときは、グローバル変数初期化の問題を回避するには、['pthread_once()'](http://linux.die.net/man/3/pthread_once)を使ってください。あなたの "ポータブル"の定義に。 –

+0

私は最良の解決策は 'boost:shared_mutex'で、読み取り専用を各スレッドにコピーするのがよいと思います。 – Pubby

答えて

2

あなたの説明から、あなたがThreadParameter(またはいくつかの他のデータ構造への書き込みをしているようです)子スレッドを開始する前に、ThreadParameterに再度書き込むことはありません。必要に応じて読み込みが行われますが、初期化後は変更されません。あれは正しいですか?そうであれば、子スレッドがデータを読みたいとき、あるいはその問題について初めてのときでも、スレッド同期システムコール(またはプロセッサ/コンパイラプリミティブ)を使用する必要はありません。

揮発性の処理は、コンパイラによって異なります。私は少なくともPowerPCのDiabでは、揮発性の処理に関するコンパイラオプションがあることを知っています:変数への読み書きのたびにPowerPC EIEIO(またはMBAR)命令を使うか、それを使わないでください...変数に関連するコンパイラの最適化を禁止することに加えて、 (EIEIO/MBARは、プロセッサ自体によるI/Oの再命令を禁止するためのPowerPCの命令であり、すなわち、命令の後のすべてのI/Oが命令の後のI/Oの前に完了しなければならない)。

正確性/安全性の観点からは、それを揮発性と宣言するのは害ではありません。しかし、実用的な観点からは、ThreadParameterをStartThread()よりもずっと早く初期化すると、volatileを宣言することは本当に必要ではないはずです(そうしないと、後続のすべてのアクセスが高速になります)。かなりの関数呼び出し(printf()やcout、システム呼び出しなど)は、プロセッサがずっと前に書込みを処理していなかったことを保証するために必要以上に多くの命令を発行しますStartThread()を呼び出す前のThreadParameter。現実には、問題のスレッドが実際に起動する前に、StartThread()自体が十分な命令をほぼ確実に実行します。だから私はあなたが本当にそれを揮発性宣言する必要はないことを示唆している、おそらくStartThread()を呼び出す前に直ちにそれを初期化しても。

メインスレッドを実行しているプロセッサが初期化を実行する前に、その変数を含むページがすでに両方のプロセッサのキャッシュにロードされていた場合にどうなるかについての質問はこちらです:一般的に利用可能な汎用プラットフォーム同様の種類のCPUを使用する場合は、キャッシュの一貫性を処理するためのハードウェアがすでに用意されている必要があります。マルチプロセッサであるかどうかにかかわらず、汎用プラットフォーム上のキャッシュ・コヒーレンシーに問題がある場所は、プロセッサに個別の命令&データ・キャッシュがあり、自己変更コードを書き込む場合です。メモリーに書き込まれる命令は、データと区別できず、そのため、CPUは命令キャッシュ内のそれらの位置を無効にしないので、命令キャッシュ内のそれらの位置を無効にしない限り、命令キャッシュに古い命令が存在する可能性があります(プロセッサ固有のアセンブリ命令お使いのOSとスレッドの特権レベルに応じて実行することが許可されているか、OSに適切なキャッシュ無効化システムコールを発行してください)。しかし、あなたが記述しているのは自己修正コードではないので、あなたはその点で安全でなければなりません。

あなたの質問1は、すべてのプロセッサー・アーキテクチャーでこれを安全にする方法を尋ねます。上記のように、データバスが適切にブリッジされているような種類のプロセッサを使用している場合は、安全でなければなりません。マルチプロセッサ相互接続用に設計された汎用プロセッサは、スレッドメモリが共有メモリ領域を適切に構成する限り、共有メモリへの書き込みを検出するためのバススヌーププロトコルを備えています。組み込みシステムで作業している場合は、BSPでそれを設定する必要があります... PowerPCの場合は、MMU/BAT設定のWIMGビットを調べる必要があります。私は他のアーキテクチャに慣れていないので、それらにあなたに指摘してください。しかし、あなたのシステムが自家製であるか、あなたのプロセッサーが同種のものでない場合、2つのプロセッサーがお互いの書き込みを詮索することができない場合があります。ハードウェア関係者にアドバイスを求めてください。

+0

ThreadParameter _once_のみを記述し、よく振舞う言語とコンパイラ(C++)を使用していると仮定して、可能な限り遅く、できれば初期化して** const **にしてください。メインスレッドでThreadParameterを変更し、その変更を尊重するために子スレッドが必要になることを期待している場合は、変数に待機条件を設定してみてください。メインスレッドがThreadParameterを使用する前に書き込むのを待つでしょうし、子スレッドが実行されるときに保留中の新しい更新をチェックすることができます。よく書かれた手順は、元の状態に戻り、更新された値を使用して再実行することができます。 –

3

新しいスレッドを作成すると、スレッドの構成は、スレッドの開始と同期します。スレッドが作成される前にThreadParameterに書き込むと、スレッドは開始後にアクセスするので、の書き込みがの前に起こることを確認することができます。正しい値。

(コンパイラは、スレッドが開始される前に行われたすべての書き込みは新しいスレッド内で表示されることを保証するために必要とされる。)

1
  1. はい、安全です。
  2. わかりません。多分:if(ThreadParameter = 5) StartThread();。しかし、一般に、コンパイラを2番目に推測しないようにしてください。
  3. おそらくそうではありません。コードを書くときにそのような低レベルの詳細を心配する必要があるなら、マルチコアマシン上でプログラムがどのように実行されるかを制御するロジックはおそらくその仕事をうまくやっていないでしょう。
  4. Boostは、マルチスレッド環境で複雑なタイプを扱う際のお友達です。