2016-12-16 25 views
7

boost::shared_mutex hoặc std::shared_mutex (C++ 17) có thể được sử dụng cho người viết đơn, truy cập nhiều đầu đọc. Là một bài tập giáo dục, tôi đã cùng nhau triển khai thực hiện đơn giản sử dụng spinlocking và có những hạn chế khác (ví dụ: chính sách công bằng), nhưng rõ ràng không được sử dụng trong các ứng dụng thực tế.C++ shared_mutex implementation

Ý tưởng là mutex giữ số tham chiếu bằng 0 nếu không có luồng nào giữ khóa. Nếu> 0, giá trị đại diện cho số lượng người đọc có quyền truy cập. Nếu -1, một người viết duy nhất có quyền truy cập.

Đây có phải là cách triển khai chính xác (đặc biệt với các thứ tự bộ nhớ tối thiểu, đã sử dụng) không có các cuộc đua dữ liệu?

#include <atomic> 

class my_shared_mutex { 
    std::atomic<int> refcount{0}; 
public: 

    void lock() // write lock 
    { 
     int val; 
     do { 
      val = 0; // Can only take a write lock when refcount == 0 

     } while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire)); 
     // can memory_order_relaxed be used if only a single thread takes write locks ? 
    } 

    void unlock() // write unlock 
    { 
     refcount.store(0, std::memory_order_release); 
    } 

    void lock_shared() // read lock 
    { 
     int val; 
     do { 
      do { 
       val = refcount.load(std::memory_order_relaxed); 

      } while (val == -1); // spinning until the write lock is released 

     } while (!refcount.compare_exchange_weak(val, val+1, std::memory_order_acquire)); 
    } 

    void unlock_shared() // read unlock 
    { 
     refcount.fetch_sub(1, std::memory_order_relaxed); 
    } 
}; 

Trả lời

3

(Tôi đang sử dụng cmpxchg như là viết tắt cho C++ compare_exchange_weak chức năng, không phải là cmpxchg instruction).

lock_shared chắc chắn có vẻ tốt: quay trên đọc và chỉ cố gắng cmpxchg khi giá trị tốt hơn nhiều cho hiệu suất hơn là quay trên cmpxchg. Mặc dù tôi nghĩ rằng bạn đã bị buộc vào đó cho đúng đắn, để tránh thay đổi -1 đến 0 và mở khóa một ghi-lock.

Tôi nghĩ rằng unlock_shared nên sử dụng mo_release, không phải mo_relaxed, vì nó cần đặt hàng tải từ cấu trúc dữ liệu được chia sẻ để đảm bảo rằng người viết không bắt đầu viết trước khi tải từ phần quan trọng của người đọc xảy ra. (LoadStore reordering là một điều về kiến ​​trúc được đặt hàng yếu, mặc dù x86 chỉ StoreLoad sắp xếp lại.) A Release operation will order preceding loads and keep them inside the critical section.


(trong ghi lock): // thể memory_order_relaxed được sử dụng nếu chỉ một chủ đề duy nhất cần viết khóa?

Không, bạn vẫn cần ghi lại bên trong phần quan trọng, vì vậy cmpxchg vẫn cần đồng bộ hóa với (trong thuật ngữ C++) từ unlock_shared.

+0

Tôi không chắc chắn về thứ tự bộ nhớ trong unlock_shared, nhưng lý do của tôi là nó không thực sự 'phát hành' bất cứ điều gì vì nó có quyền truy cập chỉ đọc và không thể thay đổi dữ liệu nó bảo vệ – LWimsey

+0

@LWimsey: vâng, khó hơn để suy nghĩ về thứ tự tải hơn so với thứ tự lưu trữ, nhưng đó là một điều thực sự. Một tải sẽ trở thành trên toàn cầu có thể nhìn thấy tại thời điểm khi nó đọc dữ liệu từ bộ nhớ cache L1. (Bởi vì đó là khi nó tạo một bản sao từ bộ nhớ đệm toàn cục kết hợp vào lõi không theo thứ tự của một CPU đơn.) –

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