2012-03-21 18 views
33

Các ngây thơ boolean phủLàm thế nào để phủ nhận nguyên tử một std :: atomic_bool?

std::atomic_bool b; 
b = !b; 

dường như không phải là nguyên tử. Tôi nghi ngờ điều này là bởi vì operator! kích hoạt một diễn viên để đồng bằng bool. Làm thế nào để thực hiện một cách nguyên tử sự phủ định tương đương? Các mã sau minh họa rằng sự phủ định ngây thơ không phải là nguyên tử:

#include <thread> 
#include <vector> 
#include <atomic> 
#include <iostream> 

typedef std::atomic_bool Bool; 

void flipAHundredThousandTimes(Bool& foo) { 
    for (size_t i = 0; i < 100000; ++i) { 
    foo = !foo; 
    } 
} 

// Launch nThreads std::threads. Each thread calls flipAHundredThousandTimes 
// on the same boolean 
void launchThreads(Bool& foo, size_t nThreads) { 

    std::vector<std::thread> threads; 
    for (size_t i = 0; i < nThreads; ++i) { 
    threads.emplace_back(flipAHundredThousandTimes, std::ref(foo)); 
    } 

    for (auto& thread : threads) thread.join(); 

} 

int main() { 

    std::cout << std::boolalpha; 
    Bool foo{true}; 

    // launch and join 10 threads, 20 times. 
    for (int i = 0; i < 20; ++i) { 
    launchThreads(foo, 10); 
    std::cout << "Result (should be true): " << foo << "\n"; 
    } 

} 

Mã này ra mắt 10 chủ đề, mỗi trong số đó flips các atomic_bool một larrge, thậm chí, số lần (100000), và in ra boolean. Điều này được lặp lại 20 lần.

EDIT: Đối với những người muốn chạy mã này, tôi đang sử dụng ảnh chụp GCC 4.7 trên ubuntu 11.10 với hai lõi. Các tùy chọn biên dịch là:

-std=c++0x -Wall -pedantic-errors -pthread 
+0

Nhìn vào thông số kỹ thuật, các loại nguyên tử như 'std :: atomic_bool' và 'std :: nguyên tử ' không có toán tử boolean như 'toán tử!'. Vì vậy, '! B' thực sự liên quan đến toán tử chuyển đổi (ẩn) của các kiểu nguyên tử. Tôi không làm câu trả lời này vì tôi không chắc chắn về cách cung cấp chức năng bạn cần. –

+0

@LucDanton Quyền, và tôi không thể tìm thấy bất cứ điều gì về một nhà điều hành 'chuyên biệt! 'Cho nguyên tử, vì vậy tôi chắc chắn rằng sự chuyển đổi xảy ra, và nó rất có thể là nguyên nhân của cuộc đua. – juanchopanza

+1

Đây là những gì tôi nhận được khi tôi chạy chương trình của bạn: 'chấm dứt được gọi sau khi ném một thể hiện của 'std :: system_error'' –

Trả lời

25

b = !b không phải là nguyên tử bởi vì nó bao gồm cả đọc và ghi, mỗi trong số đó là một hoạt động nguyên tử.

Có hai tùy chọn để sử dụng:

  1. Thay vì atomic<bool>, sử dụng một loại không thể thiếu (ví dụ atomic<int>) mà có thể là 0 hoặc 1, và xor nó với 1:

    std::atomic<int> flag(0); 
    
    flag ^= 1; //or flag.fetch_xor(1); 
    

    Thật không may, fetch_xor không được cung cấp trên atomic<bool>, chỉ trên các loại tích phân.

  2. Thực hiện một so sánh/hoạt động trao đổi trong vòng một, cho đến khi nó thành công:

    std::atomic<bool> flag(false); 
    
    bool oldValue = flag.load(); 
    while (!flag.compare_exchange_weak(oldValue, !oldValue)) {} 
    
+0

Điều thứ hai dường như gây ra livelock, ít nhất là về lý thuyết. – zch

+4

@zch: Tôi không nghĩ vậy. Nó sẽ chỉ cần lặp lại nếu một luồng khác đã sửa đổi thành công biến đó. Vì vậy, một số công việc luôn được thực hiện trên ít nhất một chủ đề - nói cách khác, điều này là không có khóa nhưng không phải chờ đợi. – interjay

+0

Tùy chọn 2 hoạt động độc đáo với ví dụ của tôi. Đây có phải là mẫu chuẩn cho các hoạt động cập nhật trên các loại nguyên tử không? – juanchopanza

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