2016-02-23 19 views
5

Tôi cố gắng để sử dụng đọc/ghi khóa trong C++ sử dụng shared_mutexđọc Viết thực hiện khóa trong C++

typedef boost::shared_mutex Lock; 
typedef boost::unique_lock<Lock> WriteLock; 
typedef boost::shared_lock<Lock> ReadLock; 

class Test { 
    Lock lock; 
    WriteLock writeLock; 
    ReadLock readLock; 

    Test() : writeLock(lock), readLock(lock) {} 

    readFn1() { 
     readLock.lock(); 
     /* 
      Some Code 
     */ 
     readLock.unlock(); 
    } 

    readFn2() { 
     readLock.lock(); 
     /* 
      Some Code 
     */ 
     readLock.unlock(); 
    } 

    writeFn1() { 
     writeLock.lock(); 
     /* 
      Some Code 
     */ 
     writeLock.unlock(); 
    } 

    writeFn2() { 
     writeLock.lock(); 
     /* 
      Some Code 
     */ 
     writeLock.unlock(); 
    } 
} 

Mã này dường như được làm việc tốt nhưng tôi có một vài câu hỏi khái niệm.

Q1. Tôi đã thấy các khuyến nghị để sử dụng unique_lock và shared_lock trên http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, nhưng tôi không hiểu tại sao vì shared_mutex đã hỗ trợ các phương thức lock và shared_shared?

Q2. Mã này có tiềm năng gây ra nạn đói viết không? Nếu có thì làm thế nào tôi có thể tránh được nạn đói?

Q3. Có khóa học nào khác mà tôi có thể thử thực hiện khóa ghi đọc không?

+1

Nếu bạn quan sát việc ghi đói (hoặc đọc đói), hãy báo cáo lỗi. Có tồn tại các thuật toán mà đảm bảo đói không xảy ra, và đây là lý do tại sao không có API ưu tiên người đọc/nhà văn trong mã này. Xem http://stackoverflow.com/a/14307116/576911 để biết các liên kết đến các thuật toán đó. Dưới đây là liên kết đến bài báo có lý do và triển khai từng phần: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp –

Trả lời

2

Q1: sử dụng một wrapper mutex

Các khuyến nghị sử dụng một đối tượng wrapper thay vì quản lý mutex trực tiếp là để tránh tình trạng đáng tiếc nơi mã của bạn bị gián đoạn và các mutex không được giải phóng, để lại nó bị khóa mãi mãi.

Đây là nguyên tắc RAII.

Nhưng điều này chỉ hoạt động nếu ReadLock hoặc WriteLock của bạn là cục bộ cho hàm sử dụng nó.

Ví dụ:

readFn1() { 
    boost::unique_lock<Lock> rl(lock); 
    /* 
     Some Code 
     ==> imagine exception is thrown 
    */ 
    rl.unlock(); // this is never reached if exception thrown 
} // fortunately local object are destroyed automatically in case 
    // an excpetion makes you leave the function prematurely  

Trong mã của bạn này sẽ không hoạt động nếu một trong những chức năng được interupted, becaus đối tượng ReadLock WriteLock của bạn là một thành viên của thử nghiệm và không địa phương để các thiết lập khóa chức năng.

Q2: Viết đói

Nó không phải là hoàn toàn rõ ràng làm thế nào bạn sẽ gọi các độc giả và các nhà văn, nhưng có, có một nguy cơ:

  • miễn là độc giả đang hoạt động, các nhà văn bị chặn bởi unique_lock chờ mutex được hấp dẫn trong chế độ độc quyền.
  • tuy nhiên chừng nào người viết đang đợi, người đọc mới có thể có quyền truy cập vào khóa được chia sẻ, khiến cho unique_lock bị trì hoãn thêm.

Nếu bạn muốn tránh đói, bạn phải đảm bảo rằng người viết đang đợi có cơ hội đặt unique_lock của họ. Ví dụ, bạn đọc một số mã để kiểm tra xem một người viết có đang đợi trước khi thiết lập khóa hay không.

lớp khóa Q3 khác

Không khá chắc chắn những gì bạn đang tìm kiếm, nhưng tôi có ấn tượng rằng condition_variable có thể được quan tâm cho bạn. Nhưng logic có một chút khác biệt.

Có lẽ, bạn cũng có thể tìm ra giải pháp bằng cách suy nghĩ ra khỏi hộp: có lẽ có cấu trúc dữ liệu không có khóa thích hợp có thể tạo điều kiện cho người đọc và nhà văn cùng tồn tại bằng cách thay đổi một chút cách tiếp cận?

2

Các loại cho khóa là ok nhưng thay vì có chúng như là chức năng thành viên tạo ra sau đó bên trong các chức năng thành viên locktype lock(mymutex). Bằng cách đó họ được phát hành trên sự hủy diệt ngay cả trong trường hợp ngoại lệ.

+0

nên đọc ... là thành viên, chức năng tạo ra chúng .. vv – systemcpro

+0

ok. Tôi lúng túng cái lol đó. Vì vậy, tôi sẽ thử viết nó bằng tiếng Anh :) – systemcpro

+0

Bên trong mỗi hàm thành viên, bạn tạo một thể hiện của kiểu khóa mong muốn trên ngăn xếp 'shared_lock (mylock)'. Không cần phải giải phóng nó, nó được giải phóng khi nó nằm ngoài phạm vi tại lối ra chức năng. Điều này làm việc ngay cả trong trường hợp ngoại lệ.Hy vọng đó là một chút giống như tiếng Anh thích hợp. Đó là một ngày dài: ( – systemcpro

1

Q1. Tôi đã thấy các khuyến nghị để sử dụng unique_lock và shared_lock trên http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, nhưng tôi không hiểu tại sao vì shared_mutex đã hỗ trợ các phương thức lock và shared_shared?

Có thể vì unique_lock đã có từ khoảng từ C++ 11 nhưng shared_lock sắp xuất hiện với C++ 17. Ngoài ra, [có thể] unique_lock có thể hiệu quả hơn. Đây là lý do ban đầu cho shared_lock [của tác giả] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html và tôi trì hoãn điều đó.

Q2. Mã này có tiềm năng gây ra nạn đói viết không? Nếu có thì làm thế nào tôi có thể tránh được nạn đói?

Có, tuyệt đối. Nếu bạn làm:

while (1) 
    writeFn1(); 

Bạn có thể kết thúc với một dòng thời gian của:

T1: writeLock.lock() 
T2: writeLock.unlock() 

T3: writeLock.lock() 
T4: writeLock.unlock() 

T5: writeLock.lock() 
T6: writeLock.unlock() 

... 

Sự khác biệt T2-T1 được tùy ý, dựa trên khối lượng công việc đang được thực hiện. Nhưng, T3-T2 gần bằng không. Đây là cửa sổ cho một luồng khác để lấy khóa. Vì cửa sổ quá nhỏ nên có lẽ sẽ không có.

Để giải quyết điều này, phương pháp đơn giản nhất là chèn một giấc ngủ nhỏ (ví dụ: nanosleep) giữa T2T3. Bạn có thể làm điều này bằng cách thêm nó vào cuối của writeFn1.

Các phương pháp khác có thể liên quan đến việc tạo hàng đợi cho khóa. Nếu một luồng không thể lấy được khóa, nó sẽ tự thêm vào hàng đợi và luồng đầu tiên trên hàng đợi lấy khóa khi khóa được giải phóng. Trong hạt nhân Linux, điều này được triển khai cho "ổ khóa được xếp hàng đợi"

Q3. Có khóa học nào khác mà tôi có thể thử thực hiện khóa ghi đọc không?

Trong khi không phải là lớp học, bạn có thể sử dụng pthread_mutex_lockpthread_mutex_unlock. Chúng thực hiện các khóa đệ quy. Bạn có thể thêm mã của riêng mình để triển khai tương đương với boost::scoped_lock. Lớp học của bạn có thể kiểm soát ngữ nghĩa.

Hoặc, boost có khóa riêng.

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