Vì vậy, chúng tôi đang sử dụng phiên bản tăng cũ hiện nay và cho đến khi nâng cấp, tôi cần để có hoạt động CAS nguyên tử trong C++ cho mã của tôi. (Chúng tôi không sử dụng C++ 0x nhưng một trong hai)So sánh và hoán đổi trong C++
tôi tạo ra các chức năng cas sau:
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;
}
Mã của tôi trong đó sử dụng các chức năng có phần như sau:
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();
}
My kiểm tra mã này sinh ra 10 luồng và báo hiệu tất cả chúng để gọi hàm với biến số cùng biến masterDeserialize.
Điều này hoạt động tốt hầu hết thời gian, nhưng mỗi một vài nghìn - vài triệu lần thử nghiệm 2 chủ đề có thể vừa nhập vào đường dẫn lấy khóa MASTER.
Tôi không chắc chắn cách thức này có thể thực hiện được hoặc cách tránh điều đó.
Tôi cố gắng sử dụng hàng rào bộ nhớ trước khi đặt lại masterDeserialize, nghĩ rằng cpu OOO có thể ảnh hưởng, nhưng điều này không ảnh hưởng đến kết quả.
Rõ ràng điều này chạy trên một máy có nhiều lõi và được biên dịch trong chế độ gỡ lỗi, vì vậy GCC không nên sắp xếp lại việc thực thi để tối ưu hóa.
Bất kỳ đề xuất nào về những gì sai với những điều trên?
EDIT: Tôi đã thử sử dụng gcc nguyên thủy thay vì mã assembly, có cùng kết quả.
inline uint32_t CAS(volatile uint32_t *mem, uint32_t with, uint32_t cmp)
{
return __sync_val_compare_and_swap(mem, cmp, with);
}
Tôi đang chạy trên một máy đa lõi, đa CPU, nhưng đó là máy ảo, có thể hành vi này do máy ảo gây ra không?
Làm cách nào để bạn biết cả hai thực tế đều đang nhập cùng một đường dẫn mã cùng một lúc? Đầu ra gỡ lỗi của bạn không được bảo vệ bởi bất kỳ khóa nào. Ngoài ra, chỉ vì lợi ích đầy đủ và nó chỉ quan trọng đối với kiến trúc có thứ tự yếu (và điều này trông rất giống với x86/x86-64): Thiết lập của 'masterDeserialize' cũng phải là một hoạt động nguyên tử. – JustSid
1. Đường dẫn mã: tất cả các chủ đề gọi hàm này, được tải và chờ một số cờ được chia sẻ giữa chúng. Tôi đặt cờ đó thành true từ một chuỗi điều khiển. 2. Đây là kiến trúc x86-64, tôi biên dịch thành 32 bit cho chương trình này. 3. Chỉ cần thiết lập masterDeserialize là nguyên tử, nhưng tôi muốn chỉ có 1 chuỗi được tìm thấy trong đường dẫn mã của masterDeserialize == true đồng thời, do đó chỉ cần thiết lập nó là không đủ - tôi cần phải kiểm tra giá trị cũ để mỗi chuỗi sẽ biết con đường để đi. –
Có thể cũng đáng nói rằng một khi một chuỗi duy nhất nhập với masterDeserialize = true, không có luồng nào khác trong vòng lặp đó nên làm như vậy. (phần mã thực hiện một số deserialization và thiết lập một con trỏ để không null, khi con trỏ đó không phải là null dòng chảy không đi vào hành động Một điều đáng nói đến là thông tin gỡ lỗi là có để gỡ lỗi một vấn đề trong Mã số được trưng bày bởi hành vi kỳ quặc như vậy –