7

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?

+0

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

+0

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. –

+0

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 –

Trả lời

1

Không chỉ hai nhưng bất kỳ số lượng chủ đề nào có thể về lý thuyết trở thành "thạc sĩ" trong mã này. Vấn đề là các chủ đề mà con đường chính sau khi hoàn thành đặt biến số masterDeserialize trở lại 0, do đó làm cho nó có thể "có được" một lần nữa bởi một sợi có thể đến rất muộn để CAS (ví dụ như do tiền thưởng). Sửa chữa thực sự đơn giản - thêm trạng thái thứ ba (với giá trị là 2) vào cờ này có nghĩa là "master đã hoàn thành" và sử dụng trạng thái này (thay vì trạng thái ban đầu là 0) ở cuối con đường của chủ nhân để báo hiệu công việc của nó được thực hiện. Do đó, chỉ một trong số các chủ đề gọi là myFunc có thể thấy 0, cung cấp cho bạn sự bảo đảm mà bạn cần. Để sử dụng lại cờ, bạn cần phải rõ ràng khởi tạo lại nó thành 0.

+0

Cảm ơn Alexey! Tôi cảm thấy thực sự ngu ngốc vì đã bỏ lỡ điều này. –