2013-06-04 50 views
6

Tôi có một thư viện C++, nghĩa vụ phải thực hiện một số phép tính trên nhiều luồng. Tôi đã tạo mã chuỗi độc lập (nghĩa là không có biến được chia sẻ giữa chúng), ngoại trừ một mảng. Vấn đề là, tôi không biết làm thế nào để làm cho nó thread-an toàn.Mảng an toàn không có khóa an toàn

Tôi nhìn vào khóa/mở khóa mutex (QMutex, khi tôi đang sử dụng Qt), nhưng nó không phù hợp với nhiệm vụ của tôi - trong khi một chủ đề sẽ khóa mutex, các chủ đề khác sẽ đợi!

Sau đó, tôi đọc khoảng std::atomic, trông giống như những gì tôi cần. Tuy nhiên, tôi cố gắng sử dụng nó theo cách sau:

std::vector<std::atomic<uint64_t>> *myVector; 

Và nó tạo ra lỗi biên dịch (sử dụng chức năng xóa 'std :: nguyên tử :: nguyên tử (const std :: nguyên tử &)). Sau đó, tôi tìm thấy the solution - sử dụng wrapper đặc biệt cho std::atomic. Tôi cố gắng này:

struct AtomicUInt64 
{ 
    std::atomic<uint64_t> atomic; 

    AtomicUInt64() : atomic() {} 

    AtomicUInt64 (std::atomic<uint64_t> a) : atomic (atomic.load()) {} 

    AtomicUInt64 (AtomicUInt64 &auint64) : atomic (auint64.atomic.load()) {} 

    AtomicUInt64 &operator= (AtomicUInt64 &auint64) 
    { 
       atomic.store (auint64.atomic.load()); 
    } 
}; 

std::vector<AtomicUInt64> *myVector; 

điều này biên dịch thành công, nhưng khi tôi không thể lấp đầy vector:

myVector = new std::vector<AtomicUInt64>(); 

for (int x = 0; x < 100; ++x) 
{ 
    /* This approach produces compiler error: 
    * use of deleted function 'std::atomic<long long unsigned int>::atomic(const std::atomic<long long unsigned int>&)' 
    */ 
    AtomicUInt64 value(std::atomic<uint64_t>(0)) ; 
    myVector->push_back (value); 

    /* And this one produces the same error: */ 
    std::atomic<uint64_t> value1 (0); 
    myVector->push_back (value1); 
} 

Tôi đang làm gì sai? Tôi cho rằng tôi đã thử tất cả mọi thứ (có thể không, dù sao) và không có gì giúp đỡ. Có cách nào khác để chia sẻ mảng an toàn trong C++ không?

Nhân tiện, tôi sử dụng trình biên dịch 32 bit MinGW 32 bit trên Windows.

+0

Bạn có muốn một mảng có kích thước cố định (phần đa luồng) của _shared elements_ hoặc _shared array_ của phần tử hay không. Tôi có nghĩa là, làm chèn và xóa trong mảng có vị trí trong _multithreaded_ một phần của mã? – Lol4t0

+0

Mảng có kích thước cố định - Tôi không có chèn hoặc xóa trong chuỗi. – ahawkthomas

+0

@ahawkthomas: Bạn push_back, thường thay đổi kích thước ... – PlasmaHH

Trả lời

5

Đây là một phiên bản làm sạch loại AtomicUInt64 của bạn:

template<typename T> 
struct MobileAtomic 
{ 
    std::atomic<T> atomic; 

    MobileAtomic() : atomic(T()) {} 

    explicit MobileAtomic (T const& v) : atomic (v) {} 
    explicit MobileAtomic (std::atomic<T> const& a) : atomic (a.load()) {} 

    MobileAtomic (MobileAtomic const&other) : atomic(other.atomic.load()) {} 

    MobileAtomic& operator=(MobileAtomic const &other) 
    { 
    atomic.store(other.atomic.load()); 
    return *this; 
    } 
}; 

typedef MobileAtomic<uint64_t> AtomicUInt64; 

và sử dụng:

AtomicUInt64 value; 
myVector->push_back (value); 

hay:

AtomicUInt64 value(x); 
myVector->push_back (value); 

vấn đề của bạn là bạn mất một std::atomic theo giá trị , gây ra một bản sao bị chặn. Ồ, và bạn đã không quay trở lại từ số operator=. Tôi cũng đã làm một số nhà thầu rõ ràng, có lẽ không cần thiết. Và tôi đã thêm const vào hàm tạo bản sao của bạn.

Tôi cũng sẽ bị cám dỗ để thêm storeload phương pháp để MobileAtomic đó chuyển tiếp đến atomic.storeatomic.load.

1

Bạn đang cố gắng sao chép một loại không thể sao chép: phương thức khởi tạo AtomicUInt64 có giá trị atomic theo giá trị.

Nếu bạn cần nó có thể khởi tạo từ atomic, thì cần tham số bằng tham chiếu (const). Tuy nhiên, trong trường hợp của bạn, có vẻ như bạn không cần khởi tạo từ số atomic; tại sao không khởi tạo từ uint64_t thay thế?

Cũng là một vài điểm nhỏ:

  • Các constructor sao chép và toán tử gán nên mất giá trị của mình bằng cách const tham khảo, để cho phép là tạm thời để được sao chép.

  • Phân bổ véc tơ với new là một điều khá kỳ lạ; bạn chỉ cần thêm một mức độ khác biệt của indirection mà không có lợi ích.

  • Đảm bảo bạn không bao giờ thay đổi kích thước mảng trong khi các chủ đề khác có thể truy cập vào nó.

+0

Cảm ơn bạn rất nhiều vì giải thích điểm nhỏ, và đặc biệt là tham chiếu 'const', tôi không biết về những điều cơ bản như vậy. – ahawkthomas

1

Dòng này

AtomicUInt64 (std::atomic<uint64_t> a) : atomic (atomic.load()) {} 

Bạn đang hoàn toàn bỏ qua các đối số bạn vượt qua trong, Có thể bạn muốn nó được a.load() và có thể bạn muốn đưa các yếu tố tham chiếu const vì vậy họ không được sao chép.

AtomicUInt64 (const std::atomic<uint64_t>& a) : atomic (a.load()) {} 

Đối với những gì bạn đang làm, tôi không chắc liệu điều đó có đúng không.Việc sửa đổi các biến bên trong mảng sẽ là nguyên tử, nhưng nếu vector được sửa đổi hoặc phân bổ lại (có thể với push_back), thì không có gì để đảm bảo sửa đổi mảng của bạn sẽ hoạt động giữa các luồng và nguyên tử.