10

ここで説明する状況は、iPad 4(ARMv7s)で発生しており、posixlibsを使用してロック/ロック解除をミューテックスします。ただし、他のARMv7デバイスでも同様のことが見られます(以下を参照)。したがって、どのソリューションでも、ARMv7のミューテックスとメモリフェンスの動作をより一般的に調べる必要があると思います。

シナリオの擬似コード:

スレッド1–データの生成:

void ProduceFunction() {
  MutexLock();
  int TempProducerIndex = mSharedProducerIndex; // Take a copy of the int member variable for Producers Index
  mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
  mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
  MutexUnlock();
}

スレッド2–データの消費:

void ConsumingFunction () {
  while (mConsumerIndex != mSharedProducerIndex) {
    doWorkOnData (mSharedArray[mConsumerIndex++]);
  }
}

以前(iPad 2で問題が発生したとき)、私はmSharedProducerIndex = TempProducerIndexそれがアトミックに実行されていないと信じていたので、AtomicCompareAndSwapを割り当てるためにを使用するように変更しましたmSharedProducerIndex。これはこの時点まではうまくいきましたが、私が間違っていたことが判明し、バグが戻ってきました。「修正」はタイミングを変えただけだと思います。

私は今、実際の問題はミューテックスロック内での書き込みのアウトオブオーダー実行であるという結論に達しました。つまり、コンパイラまたはハードウェアのいずれかが並べ替えを決定した場合です。

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
mSharedProducerIndex = TempProducerIndex;  // Signal consumer data is ready by assigning new Producer Index to shared variable

... に:

mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable
mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 

...そして、コンシューマーがプロデューサーをインターリーブした場合、コンシューマーがデータを読み取ろうとしたときに、データはまだ書き込まれていませんでした。

したがって、メモリバリアについて読んだ後、信号を外部の消費者に移動してみようと思いましmutex_unlockた。ロックを解除すると、次の場所に確実mSharedArrayに書き込まれるメモリバリア/フェンスが生成されると信じていました。

mSharedArray[TempProducerIndex++] = NewData;  // Copy new Data into array at Temp Index 
MutexUnlock();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable

mutex_unlockしかし、これはまだ失敗し、aが確実に書き込みフェンスとして機能するかどうか疑問に思いますか?

また、コンパイラがコードをsに移動できる(ただし、sから移動できない)ことを示唆するHPの記事も読みましたcrit_sec。したがって、上記の変更後でも、書き込みはmSharedProducerIndexバリアの前にある可能性があります。この理論にマイレージはありますか?

明示的なフェンスを追加することで、問題は解消されます。

mSharedArray[TempProducerIndex++] = NewData; // Copy new Data into array at Temp Index 
OSMemoryBarrier();
mSharedProducerIndex = TempProducerIndex; // Signal consumer data is ready by assigning new Producer Index to shared variable

したがって、私は問題を理解しており、フェンスが必要であると思いますが、ロック解除の動作と、それがバリアを実行していないように見える理由についての洞察は非常に役立ちます。

編集:

コンシューマースレッドにミューテックスがないことについて:私はint mSharedProducerIndex単一の命令であるという書き込みに依存しているため、コンシューマーが新しい値または古い値のいずれかを読み取ることを望んでいます。どちらも有効な状態であり、それmSharedArrayが順番に(つまり、書き込む前にmSharedProducerIndex)書き込まれる場合、これは問題ありませんが、これまでの説明から、これについては返信できません。

mSharedProducerIndex同じロジックにより、書き込みがバリア内に移動する可能性があり、したがって誤って並べ替えられる可能性があるため、現在のバリアソリューションにも欠陥があるように見えます。

読み取りバリアとして機能するために、コンシューマーにミューテックスを追加することをお勧めしますか、それともPPCpragmaのように、プロデューサーでアウトオブオーダー実行を無効にするための命令がありますか?EIEIO

4

2 に答える 2

7

プロデュースは同期されますが、消費時に同期は行われません(メモリもバリアと同期する必要があります)。したがって、プロデューサーに完全なメモリバリアがあるとしても、そのメモリバリアは消費者には役立ちません。

mSharedProducerIndexあなたのコードでは、スレッド#2を実行している他のコアの古い値によってさえ、コンパイラの順序、ハードウェアの順序に見舞われる可能性があります。

特に、Chapter 11: Memory OrderingCortex ™-Aシリーズプログラマーズガイドをお読みください11.2.1 Memory barrier use example

あなたの問題は、コンシューマースレッドで部分的な更新を取得していることだと思います。問題は、プロデューサーのクリティカルセクション内にあるものがアトミックではなく、並べ替えることができることです。

not atomicつまり、ワードストアでない場合(mSharedArray[TempProducerIndex++] = NewData;NewDataのタイプはint)、他のコアでは部分的な更新と見なされる可能性があるいくつかのステップで実行される可能性があります。

つまりreordering、ミューテックスは出入りの障壁を提供しますが、クリティカルセクション中に順序付けを課すことはありません。コンシューマー側には特別な構成がないため、mSharedProducerIndex更新されていることがわかりますが、の部分的な更新は表示されますmSharedArray[mConsumerIndex]。Mutexは、実行がクリティカルセクションを離れた後にのみメモリの可視性を保証します。

OSMemoryBarrier();これは、クリティカルセクション内に追加したときに機能する理由も説明していると思います。これは、CPUがデータを書き込んでmSharedArrayから更新するように強制されmConsumerIndex、他のコア/スレッドがバリアのために完全にコピーされmConsumerIndexていることを確認したためです。mSharedArray

OSMemoryBarrier();多くのプロデューサーと1つのコンシューマーがいると仮定すると、の実装は正しいと思います。プロデューサー内のクリティカルセクションで発生する部分的な更新や並べ替えは修正されないと思うので、コンシューマーにメモリバリアを設定することを提案するコメントには同意しません。

タイトルの質問への回答として、一般的に、afaik mutexesは、入る前にバリアを読み取り、出る後にバリアを書き込みます。

于 2013-02-21T13:39:09.413 に答える
6

「理論」は正しいです。書き込みは、書き込みフェンスの後から前に移動できます。

コードの基本的な問題は、スレッド2で同期がまったく行われないことです。mSharedProducerIndex読み取りバリアなしで読み取るため、どのような値が得られるかは誰にもわかりません。スレッド1で行うことは、それを解決しません。

于 2013-02-21T13:30:49.040 に答える