4

今日のコードレビューで、私は次のコードに出くわしました(投稿用に少し変更されています):

while (!initialized)
{
  // The thread can start before the constructor has finished initializing the object.
  // Can lead to strange behavior. 
  continue;
}

これは、新しいスレッドで実行されるコードの最初の数行です。別のスレッドでは、初期化が完了すると、に設定initializedされtrueます。

オプティマイザーがこれを無限ループに変える可能性があることは知っていますが、それを回避するための最良の方法は何ですか?

  • volatile-有害と考えられる
  • 変数を直接使用する代わりに関数を呼び出すisInitialized()-これはメモリバリアを保証しますか?関数が宣言された場合はどうなりますinlineか?

他に選択肢はありますか?

編集:

これについてはもっと早く言及する必要がありますが、これはWindows、Linux、Solarisなどで実行する必要のあるポータブルコードです。ポータブルスレッドライブラリには主にBoost.Threadを使用しています。

4

5 に答える 5

6

関数を呼び出してもまったく役に立ちません。関数が宣言されていない場合でも、その本体はインライン化できます(関数を別のライブラリに配置して動的にリンクするinlineなど、極端なものを除いて)。isInitialized()

頭に浮かぶ2つのオプション:

  • アトミックフラグとして宣言initializedします(C ++ 0xでは、を使用できますstd::atomic_flag。それ以外の場合は、これを行う方法について、スレッドライブラリのドキュメントを参照してください)

  • セマフォを使用します。他のスレッドで取得し、このスレッドで待機します。

于 2010-12-30T17:25:18.810 に答える
4

@Karlのコメントが答えです。スレッドBが初期化を完了するまで、スレッドAで処理を開始しないでください。これを行うための鍵は、スレッドBからスレッドAに、稼働中であるというシグナルを送信することです。

OSについて言及されていないので、Windows風の擬似コードをいくつか紹介します。選択したOS/ライブラリにトランスコードします。

まず、Windowsイベントオブジェクトを作成します。これは信号として使用されます:

スレッドA:

HANDLE running = CreateEvent(0, TRUE, FALSE, 0);

次に、スレッドAにスレッドBを開始させ、イベントをスレッドBに渡します。

スレッドA:

DWORD thread_b_id = 0;
HANDLE thread_b = CreateThread(0, 0, ThreadBMain, (void*)handle, 0, &thread_b_id);

スレッドAで、イベントが通知されるまで待ちます。

スレッドA:

DWORD rc = WaitForSingleObject(running, INFINITE);
if( rc == WAIT_OBJECT_0 )
{
  // thread B is up & running now...
  // MAGIC HAPPENS
}

スレッドBの起動ルーチンは初期化を行い、イベントを通知します。

スレッドB:

DWORD WINAPI ThreadBMain(void* param)
{
  HANDLE running = (HANDLE)param;
  do_expensive_initialization();
  SetEvent(running); // this will tell Thread A that we're good to go
}
于 2010-12-30T17:46:21.210 に答える
3

同期プリミティブは、ループで回転するのではなく、この問題の解決策です...しかし、ループで回転する必要があり、セマフォやイベントなどを使用できない場合は、を安全に使用できますvolatile。オプティマイザを傷つけるため、有害であると見なされます。この場合、それはまさにあなたがやりたいことですよね?

于 2010-12-30T17:30:16.940 に答える
0

boost::onceにはonce_flagと呼ばれるatomic_flagと同等のブーストがあります。それはあなたがここで望むものかもしれません。

事実上、遅延読み込みなど、最初に呼び出されたときに何かを構築し、複数のスレッドで発生する場合は、最初に関数に到達したときに、boost::onceを取得します。事後条件は、初期化されているため、ループやロックの必要がないことです。

確認する必要があるのは、初期化ロジックが例外をスローしないことです。

于 2010-12-30T18:15:11.333 に答える
0

これは、スレッドを操作するときによく知られている問題です。オブジェクトの作成/初期化には比較的短時間で済みます。ただし、スレッドが実際に実行を開始すると...実行されるコードに関してはかなり長い時間がかかる可能性があります。

誰もがセマフォについて言及し続けています...

POSIX1003.1bセマフォを確認することをお勧めします。Linuxでは、mansem_initを試してください。例えば:

これらのセマフォには、作成/初期化されると、あるスレッドが別のスレッドによって通知されるまで無期限にブロックできるという利点があります。さらに重要なことに、そのシグナルは、待機中のスレッドが待機を開始する前に発生する可能性があります。(セマフォ条件変数の大きな違い。)また、セマフォは、ウェイクアップする前に複数の信号を受信する状況を処理できます。

于 2010-12-30T19:11:10.570 に答える