2012-01-25 39 views
7

Đây có phải là cách triển khai chính xác cho chức năng hoán đổi nguyên tử chung không? Tôi đang tìm một giải pháp tương thích C++ 03 trên GCC.Chức năng hoán đổi nguyên tử sử dụng gcc nguyên tử dựng sẵn

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    T * ptr = &a; 
    b =__sync_lock_test_and_set(ptr, b); 
    __sync_lock_release(&ptr); 
} 

Nếu không, tôi nên làm gì để khắc phục sự cố?

Ngoài ra: là __sync_lock_release luôn cần thiết? Khi tìm kiếm thông qua các codebases khác tôi thấy rằng điều này thường không được gọi. Nếu không có việc phát hành gọi mã của tôi trông như thế này:

template<typename T> 
void atomic_swap(T & a, T & b) { 
    static_assert(sizeof(T) <= sizeof(void*), "Maximum size type exceeded."); 
    b = __sync_lock_test_and_set(&a, b); 
} 

PS: Atomic swap in GNU C++ là một câu hỏi tương tự nhưng nó không trả lời câu hỏi của tôi vì câu trả lời cung cấp đòi hỏi C++ 11 của std::atomic và nó có chữ ký Data *swap_data(Data *new_data) đó doesn' t dường như có ý nghĩa ở tất cả cho một hàm swap. (Nó thực sự hoán đổi đối số được cung cấp với một biến toàn cục đã được xác định trước hàm.)

+0

Dường như chỉ truy cập vào 'a' được cho là nguyên tử? –

+0

Tại sao lại phát minh ra bánh xe? Xem http://concurrencykit.org/ –

+0

@BenVoigt Bài đăng đó không đưa ra câu trả lời rõ ràng. Và nó hoán đổi đối số với biến toàn cục. – StackedCrooked

Trả lời

9

Hãy nhớ rằng phiên bản hoán đổi này không phải là một hoạt động nguyên tử đầy đủ. Trong khi giá trị của b sẽ được sao chép theo nguyên tử vào a, giá trị a có thể sao chép trên một sửa đổi khác với giá trị là b theo một chuỗi khác. Nói cách khác, việc gán cho b không phải là nguyên tử đối với các chủ đề khác. Do đó, bạn có thể kết thúc với tình huống là a == 1b == 2 và sau khi đã cài đặt gcc, bạn kết thúc với a == 2 và giá trị của 1 được trả lại, nhưng bây giờ một chủ đề khác đã thay đổi giá trị b thành 3 và bạn ghi lại giá trị đó trong b với giá trị là 1. Vì vậy, trong khi bạn có thể "đổi kỹ" các giá trị, bạn đã không làm điều đó một cách nguyên tử ... một chuỗi khác đã chạm giá trị của b ở giữa sự trở lại từ gcc nguyên tử được tích hợp sẵn và gán giá trị trả về đó b. Nhìn từ lắp ráp đứng điểm, bạn phải giống như sau:

lea RAX, qword ptr [RDI] // T * ptr = &a; 
mov RCX, qword ptr [RSI] // copy out the value referenced by b into a register 
xchg [RAX], RCX   // __sync_lock_test_and_set(&a, b) 
mov qword ptr [RSI], RCX // place the exchange value back into b (not atomic!!) 

Thành thật mà nói, bạn không thể làm một trao đổi nguyên tử lock-free của hai địa điểm bộ nhớ riêng biệt mà không có một hoạt động phần cứng giống như một DCAS hoặc một tải yếu liên kết/lưu trữ-có điều kiện, hoặc có thể sử dụng một số phương pháp khác như bộ nhớ giao dịch (mà bản thân nó có xu hướng sử dụng khóa hạt mịn).

Thứ hai, khi chức năng của bạn được viết ngay bây giờ, nếu bạn muốn hoạt động nguyên tử của bạn có được cả hai và phát hành ngữ nghĩa, thì có, bạn sẽ phải đặt trong số __sync_lock_release hoặc bạn sẽ phải thêm một hàng rào bộ nhớ đầy đủ thông qua __sync_synchronize. Nếu không, nó sẽ chỉ có được ngữ nghĩa trên __sync_lock_test_and_set. Tuy nhiên, nó không trao đổi hai vị trí bộ nhớ riêng biệt với nhau ...

+0

Tôi hiểu rằng việc ghi vào 'b' làm cho nó trở thành một hoạt động phi nguyên tử và do đó không an toàn cho luồng. Tuy nhiên, tôi không thấy một cách để làm cho nó an toàn. Điều này có nghĩa là việc trao đổi không khóa các loại con trỏ không thực sự có thể? – StackedCrooked

+0

Khá nhiều, trừ khi một lần nữa, phần cứng hỗ trợ một cái gì đó như DCAS, hoặc hoạt động có điều kiện tải liên kết/lưu trữ khá yếu để nó không thành công nếu có quyền truy cập vào bộ nhớ hoặc ít nhất truy cập bộ nhớ trên một dòng bộ nhớ cache và hai giá trị bạn đang trao đổi trên cùng một dòng bộ nhớ cache – Jason

Các vấn đề liên quan