2012-09-11 8 views
5

Microsoft Visual C++では、1つのvoid *パラメータを持つ関数を起動してCreateThread()を呼び出してスレッドを作成できます。私はそのパラメタとして構造体へのポインタを渡します。私は他の多くの人にもそうしています。C++ - スレッドに渡されるデータはvolatileであるべきですか?

私の構造体へのポインタを渡しているのですか?CreateThread()が呼び出される前に構造体メンバが実際にメモリに書き込まれたかどうかをどのように知っていますか?彼らが単にキャッシュされないという保証はありますか?たとえば、次のように

struct bigapple { string color; int count; } apple; 
apple.count = 1; 
apple.color = "red"; 
hThread = CreateThread(NULL, 0, myfunction, &apple, 0, NULL); 

DWORD WINAPI myfunction(void *param) 
{ 
    struct bigapple *myapple = (struct bigapple *)param; 

    // how do I know that apple's struct was actually written to memory before CreateThread? 
    cout << "Apple count: " << myapple->count << endl; 
} 

私はスレッドに揮発性のないデータを渡し、このウェブサイトおよび他のWindowsのコードの多くを見て読んでいた、と任意のメモリがあるようには思えませんが、この日の午後障壁または他の何か。私はC++を知っているか、少なくとも古いリビジョンは「スレッド認識」ではないので、別の理由があるのだろうかと思っています。私の推測では、コールの前にappleのメンバーを書き出すことを知っているので、コンパイラがCreateThread()の呼び出しでポインタ& appleを渡したことがわかります。

+0

がスレッドに参加していると思います? –

+1

これはCではなく、C++( 'struct bigapple *'?) –

答えて

5

番号: は、他のスレッドの終了を待つために、あなたのメインスレッドはちょうどので、メインスレッドがより適切になります

WaitForSingleObject(hthread,INFINITE); 

を呼び出す必要があります関連するWin32スレッド関数はすべて、必要なメモリバリアを処理します。 CreateThreadより前のすべての書き込みは、新しいスレッドに表示されます。明らかに、新しく作成されたスレッドの読み込みは、CreateThreadの呼び出しの前に並べ替えることはできません。

volatileは、コンパイラに余分な有益な制約を加えず、コードを遅くするだけです。しかし実際には、新しいスレッドを作成するコストと比較して目立つことはありません。

+0

+1私は本当に自分自身を必要としません(あなたとレイモンドは私にとって多かれ少なかれプライマリソースです:P) OPは合理的に考える必要があると思うかもしれません... – Mehrdad

+0

スレッドで私はapple-> count = 2を割り当てていましたか?スレッドが終了すると(myfunctionからの通常の戻りによって)、2がメモリに書き込まれたことが保証されます&apple-> countの位置?さらに、呼び出しコードは「実現」していますか?たとえば、CreateThread()呼び出しが成功し、次の行WaitForSingleObject(hThread、INFINITE);を作成し、次の 'cout << apple-> count << endl;を実行すると、countが2として読み取られることが保証されます。キャッシュは1ではありませんか?ありがとう。 – loop

+0

@Mehrdad:例えば、 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686355(v=vs.85).aspx – MSalters

1

まず

おかげで、私は、オプティマイザは正確を犠牲にして順番を変更することはできませんと思います。 CreateThread()は関数で、関数呼び出しのパラメータbinidngはの前に発生し、が呼び出されます。

第2に、揮発性は、目的とする目的にはあまり役に立ちません。 Check out this article

+3

最初の文は明らかです。コンパイラは、正確性を損なう場合、順序を変更しないことがあります。しかし、何が正しいですか?それはいつもあなたが期待するものではないかもしれません。例えば、2番目の文はそのような誤った仮定を示している。 'apple.count'への書き込みは、パラメータ束縛の一部ではなく、'&apple'に対して相対的に並べ替えることができます。 – MSalters

+0

@MSalters okですが、構造体へのポインタを関数パラメータとして渡し、呼び出し先が有効なデータで動作することを知る適切な方法は何ですか? 'mystruct.myvar = myval;を実行すると、スレッドを無視します。 myfunc(&mystruct); '私はmyfunc()が常にmyvalをmyvalとして読み込むという印象を受けました。あれは正しいですか? – loop

+0

@test:スレッドを無視すると、スレッド内の並べ替えがスレッドのセマンティクスを壊さないために機能します。スレッド間でこれを行う適切な方法は、メモリの障壁です。これらはスレッド同期関数に含まれています。例えば。 Windowsの場合、クリティカルセクションで 'apple 'へのアクセスを保護することも、必要なメモリバリアを処理します。 – MSalters

2

いいえ、volatileであってはいけません。同時に、あなたは有効な問題を指しています。キャッシュの詳細な動作は、Intel/ARM/etcの論文に説明されています。

しかし、データが書かれていることは間違いありませんです。そうしなければ、あまりにも多くのことが壊れるでしょう数十年の経験から、これはそうであることがわかります。

スレッドスケジューラが同じコアでスレッドを開始する場合、キャッシュの状態は正常です。そうでない場合、カーネルはキャッシュをフラッシュします。それ以外の場合、何も動作しません。

volatileをスレッド間の相互作用に使用しないでください。これは、スレッド内でのみデータを処理する方法(レジスタコピーを使用するか、常に再読み込みするかなど)に関する指示です。

+1

これに全面的に同意した。データコヒーレンシハザードは、おそらくコアクロックサイクルの10秒で短期間存在し、しばしばライトバックバッファやキャッシュプリフェッチなどのCPU最適化メカニズムが原因です。キャッシュがコア間で共有されていないSMPアーキテクチャでは、コンテキストスイッチ(非常に高価)上のキャッシュフラッシュに頼るのではなく、広範なキャッシュスヌーピング構成を持っています。 sys-callが暗黙的なメモリバリアであることを前提としています(特権モードへの切り替えでさえも、多くのアーキテクチャでこのような結果になる可能性があります)。 – marko

+0

@Kirillスレッド間のやりとりにvolatileを使用しないのはなぜですか? – loop

+0

「volatile」はこれを意図していないためです。その使用は、同じスレッドに対してのみ保証を提供します。あなたは 'volatile int a; b = a; b = a; '、コンパイラは2番目の代入を省略することはできません。 var 'a'は赤色に2回する必要があります。しかし、同時にこのvarを読み書きするスレッドはいくつですか?これは完全には指定されていません。あなたはそのような前提をすることはできません。 –

0

あなたは非問題に苦労していて、少なくとも他の2 ...

  1. を作成するには、のCreateThreadに与えられたパラメータを心配しないでください:彼らは時に存在した場合にスレッドがありますCreateThreadが返るまで存在します。そして、それらを作成するスレッドはそれらを破壊しないので、他のスレッドも利用できます。
  2. 、彼らが破壊されるとき、問題は今、誰がなって:あなたはnewでそれらを作成deleteが呼び出されるまでので、彼らが存在します(またはプロセスが終了するまで:!良いメモリリークを)
  3. ときにそのメインプロセスを終了スレッド終了(他のすべてのスレッドもOSによって終了されます)。そしてあなたのメインには、他のスレッドが完了するのを待つものはありません。
  4. CreateThreadのような低レベルのAPIを使用するときには注意してください。独自のライブラリもスレッドとインターフェイスされています。 Cランタイムは_beginthreadexです。CreateThreadが呼び出され、それ以外の場合には見逃してしまうC++ライブラリの他の初期化タスクも実行されます。一部のC(およびC++)ライブラリ関数は、終了時にランタイムリソースを適切に解放するために必要な、これらの初期化なしでは正しく動作しない場合があります。 CreateThreadを解読することはmallocを文脈で使用するようなものであり、deleteを次にクリーンアップに使用します。

適切メインスレッドbnehaviorのWin32 APIのドキュメントが明確に言っていない何

// create the data 
// create the other thread 
// // perform othe task 
// wait for the oter thread to terminate 
// destroy the data 

である必要があり、すべてのHANDLE待機可能ある、と関連付けるリソースが解放されたときに合図になるということです。

{ 
    data* pdata = new data; 
    HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, pdata,0,0); 
    WaitForSingleObject(htread,INFINITE); 
    delete pdata; 
} 

あるいは

{ 
    data d; 
    HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, &d,0,0); 
    WaitForSingleObject(htread,INFINITE); 
} 
0

私は質問が別のコンテキストで有効だと思います。 他の人が構造体を使用することを指摘し、内容は安全です(ただし、データへのアクセスは同期化する必要があります)。

しかし、スレッド外で変更できるアトミック変数(または1つのポインタ)があれば問題は有効だと思います。その場合の私の意見は、この場合には揮発性が使用されるべきであるということです。

編集: 私はあなたが印刷する前に、wikiページ上の例では、良い説明http://en.wikipedia.org/wiki/Volatile_variable

関連する問題