0

私はすでにこのように書き込みのためにミューテックスでポインタを保護しています

// thread1
if(pointer)
{
   boost::mutex::scoped_lock lock(pointer_mutex);
   if(pointer)
      pointer->DoStuff();
}

// thread2
if(pointer)
{
   boost::mutex::scoped_lock lock(pointer_mutex);
   if(pointer)
      pointer = anotherPointer;
}

// thread3
if(pointer)
{
   boost::mutex::scoped_lock lock(pointer_mutex);
   pointer = 0;
}

ポインターが 99.999 回 null であるため、そのミューテックスをブロックの外に配置したくありません。

これはクラッシュせずに正常に動作しますが、スレッドセーフと宣言するのに十分な経験がありません。

私の質問は:

if(pointer) ポインタ = 0; ポインター = anotherPointer; 原子?

ありがとうございました。

4

4 に答える 4

4

安全ではなく、「ダブルチェックロック」でもありません。この論文読んでください。

于 2012-10-23T16:11:32.470 に答える
3

データ競合を導入しているため、正式には違法です。そして、私は微妙な非アトミック読み取りについて話しているだけではなく、非常に単純に、チェックとロックの取得の間で別のスレッドがポインターを操作していた可能性があります。

ただし、この残虐行為について少なくとも少し気分を良くする方法は次のとおりです。

if (pointer)  // dirty read, eek
{
    boost::mutex::scoped_lock lock(pointer_mutex);
    if (pointer) { pointer = 0; }  // reliable
}
于 2012-10-23T16:05:14.173 に答える
1

ミューテックスをブロックの外に置きます。

boost :: mutexの内部はわかりませんが、正しく記述されていると思います。ミューテックスのロック/ロック解除は、約10サイクルで実行できます。常にロックすることは、パフォーマンスの問題としてはそれほど大きな問題にはなりません。

マルチスレッドの場合、システムを100%スレッドセーフにする必要があります。テストでは、MTシステムが100%正常に動作しているように見えることがよくありますが、使用パターンがわずかに異なるか、負荷の量が異なると、問題が発生し始めます。また、MTクラッシュは、あるスレッドでエラーが発生する可能性があるが、別のスレッドでクラッシュする可能性があるため、デバッグが非常に難しい場合があります。

@edit:複数のスレッドがポインターを使用できるようにしたいが、一度に1つのスレッドだけがポインターを変更できるようにするというパフォーマンス上の懸念はありますか?その場合は、読み取り/書き込みロックを使用してください。複数のスレッドが読み取ることができますが、1つのスレッドだけが書き込むことができ、リーダーを除外します。

読み取り/書き込みロックのオーバーヘッドは、単なるミューテックスよりも少し大きいですが、複数のスレッドが「doStuff」できるという事実によって補われています。そして、ブロックの外に読み取り/書き込みロックを置きます。

于 2012-10-23T16:13:10.987 に答える
1

(元々、OP はミューテックスを取得した後、スレッド 1 でポインターを再度チェックしませんでした。つまり、スレッド 3 によって null になった可能性があります)。

ただし、この修正を行っても、コンパイラが過剰に最適化し、チェックで検出した値を「キャッシュ」する可能性があります。(再確認されたロックの問題)。

質問に関しては、C++11 にはアトミック バージョンがあります。

if( pointer )
    pointer == 0;

アトミックではありません。これらの呼び出しの間でポインターが変わる可能性があります。

これを行う際の問題

if( pointer )
{
     mutex_lock lock( mutex );
     if( pointer )
     {
         pointer = 0;
     }
}

「最適化」しないようにコンパイラーに指示し、最初にチェックしたときと2回目にチェックしたときとの間にポインターが変更された可能性があることを認識させることです。

コンパイラの裏をかこうとする方法はいくつかあります。最も明白な方法はvolatileキーワードを使用することですが、悲しいことに、標準ではコンパイラがそれに従うことを強制していません。ポインターを返す関数を使用して、その関数を仮想または同様のものにして、コンパイラーがインライン展開するのを防ぐことができます。

または、この状況でアセンブリを使用する極端な方法を実行することもできます。

なお、場合によっては を使用してboost::onceください。

于 2012-10-23T16:14:45.083 に答える