그래서 우리는 지금 꽤 오래된 버전의 부스트를 사용하고 있습니다. 업그레이드하기 전까지는 코드에 C++로 원자 적 연산을 사용하기 위해 이 필요합니다.비교 및 스왑 C++
이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;
}
기능을 사용
내 코드는 다음과 같은 다소이다 : (우리는 하나 아직 C++ 0X를 사용하지 않는)
나는 다음과 같은 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();
}
내 이 코드를 테스트하면 10 개의 스레드가 생성되고 모두 동일한 masterDeserialize 변수를 사용하여 함수를 호출하도록 신호를 보냅니다.
이 방법은 대부분 잘 작동하지만 두 번 스레드가 모두 마스터 잠금을 획득 할 수있는 경로에 들어가는 횟수는 천 2 백만 회입니다.
나는 이것이 어떻게 가능한지, 또는 그것을 피하는 방법을 모르겠습니다.
내가 메모리 펜스를 사용하여 masterDeserialize를 재설정하기 전에 CPU OOO가 영향을 미칠 수 있다고 생각했지만 결과에 아무런 영향을주지 않습니다.
분명히 이것은 많은 코어가있는 시스템에서 실행되며 디버그 모드로 컴파일되므로 GCC는 최적화를 위해 실행 순서를 바꾸면 안됩니다.
위의 사항에 대한 잘못된 제안 사항이 있으십니까?
편집 : 어셈블리 코드 대신 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의 코드 경로에서 단 하나의 스레드 만 찾을 수 있기를 바란다. 따라서 설정만으로는 충분하지 않다. - 이전 값을 테스트하여 각 스레드가 어느 길을 취해야하는지 알기. –
하나의 스레드가 masterDeserialize = true를 사용하면 해당 반복에서 다른 스레드가 그렇게해야 함을 언급하는 것도 가치가 있습니다. (코드 섹션은 일부 비 직렬화를 수행하고 포인터가 null이 아니며 포인터가 null이 아닌 경우 흐름이 아무런 작동도 수행하지 않습니다. 언급 할 가치가있는 또 다른 사항은 디버그 정보가 그 이상한 행동에 의해 전시되는 코드 –