私たちはかなり古いバージョンのboostを使用しています。アップグレードするまでは、にC++でのアトミックなCAS操作が必要です。 (私たちはどちらかまだC++ 0xのを使用していない)C++で比較してスワップする
私は、次のCAS機能作成:
void myFunc(uint32_t &masterDeserialize)
{
std::ostringstream debugStream;
unsigned int tid = pthread_self();
debugStream << "myFunc, threadId: " << tid << " masterDeserialize= " << masterDeserialize << " masterAddress = " << &masterDeserialize << std::endl;
// memory fence
__asm__ __volatile__ ("" ::: "memory");
uint32_t retMaster = CAS(&masterDeserialize, 1, 0);
debugStream << "After cas, threadid = " << tid << " retMaster = " << retMaster << " MasterDeserialize = " << masterDeserialize << " masterAddress = " << &masterDeserialize << std::endl;
if(retMaster != 0) // not master deserializer.
{
debugStream << "getConfigurationRowField, threadId: " << tid << " NOT master. retMaster = " << retMaster << std::endl;
DO SOMETHING...
}
else
{
debugStream << "getConfigurationRowField, threadId: " << tid << " MASTER. retMaster = " << retMaster << std::endl;
DO SOME LOGIC
// Signal we're done deserializing.
masterDeserialize = 0;
}
std::cout << debugStream.str();
}
マイ:機能を使用しています私のコードは次のように多少です
inline uint32_t CAS(volatile uint32_t *mem, uint32_t with, uint32_t cmp)
{
uint32_t prev = cmp;
// This version by Mans Rullgard of Pathscale
__asm__ __volatile__ ("lock\n\t"
"cmpxchg %2,%0"
: "+m"(*mem), "+a"(prev)
: "r"(with)
: "cc");
return prev;
}
をこのコードをテストすると10個のスレッドが生成され、と同じmasterDeserialize変数を使用して関数を呼び出すようにすべてのスレッドに通知します。
これはほとんどの場合よく機能しますが、数千〜数百万回のテスト反復で2つのスレッドが両方ともマスターロックを取得するパスに入ることがあります。
私はこれがどうやって可能か、それを避ける方法はわかりません。
私はmasterDeserializeをリセットする前にメモリフェンスを使用しようとしましたが、CPU OOOが影響を及ぼす可能性があると考えましたが、これは結果に影響しません。
これは明らかに多くのコアを持つマシン上で実行され、デバッグモードでコンパイルされるため、GCCは最適化のために実行順序を変更しないでください。
上記に何が間違っていますか?
EDIT: アセンブリコードの代わりにgccプリミティブを使用しましたが、同じ結果が得られました。
inline uint32_t CAS(volatile uint32_t *mem, uint32_t with, uint32_t cmp)
{
return __sync_val_compare_and_swap(mem, cmp, with);
}
私はマルチコア、マルチCPUマシン上で実行していますが、それは、仮想マシンであり、それはこの動作はVMによって何とか引き起こされている可能性があり
?
実際に両方が同じコードパスに同時に入っていることをどのように知っていますか?あなたのデバッグ出力はロックによって保護されません。また、完全性のために、弱い順序のアーキテクチャでしか問題になりません(これはx86/x86-64とよく似ています): 'masterDeserialize'の設定はアトミックな操作でなければなりません。 – JustSid
1.コードパス:この関数を呼び出すすべてのスレッドがロードされ、それらのスレッド間で共有されるいくつかのフラグを待ちます。私は制御スレッドからそのフラグをtrueに設定しました。 2.これはx86-64アーキテクチャです。このプログラム用に32ビットとしてコンパイルします。 3. masterDeserializeをアトミックに設定するだけですが、masterDeserialize == trueのコードパスに1つのスレッドしか見つからないように設定するだけで十分ではありません。また、古い値をテストして各スレッドが取るべき道を知っている。 –
また、あるスレッドがmasterDeserialize = trueを取り込んだら、その反復の他のスレッドはそのようにするべきではありません。 (コードセクションはいくつかの逆シリアル化を行い、ポインタがnullでない場合にポインタがnullにならないように設定します) もう1つ言及したいことは、デバッグ情報がそのような奇妙な振る舞いによって示されるコード。 –