2013-12-11 17 views
229

Tôi có hai trường hợp sử dụng.std :: unique_lock <std::mutex> hoặc std :: lock_guard <std::mutex>?

A. Tôi muốn đồng bộ hóa quyền truy cập theo hai luồng vào hàng đợi.

B. Tôi muốn đồng bộ hóa quyền truy cập theo hai luồng vào hàng đợi và sử dụng biến điều kiện vì một trong các chuỗi sẽ chờ nội dung được lưu vào hàng đợi theo chuỗi khác.

Đối với trường hợp sử dụng A Tôi thấy ví dụ về mã sử dụng std::lock_guard<>. Đối với trường hợp sử dụng B, tôi xem ví dụ mã sử dụng std::unique_lock<>.

Sự khác nhau giữa hai yếu tố nào và tôi nên sử dụng loại nào trong trường hợp sử dụng nào?

Trả lời

222

Sự khác biệt là bạn có thể khóa và mở khóa std::unique_lock. std::lock_guard sẽ chỉ bị khóa một lần khi xây dựng và mở khóa khi hủy.

Vì vậy, đối với usecase B, bạn chắc chắn cần một std::unique_lock cho biến điều kiện. Trong trường hợp A nó phụ thuộc vào việc bạn cần phải đóng lại bảo vệ.

std::unique_lock có các tính năng khác cho phép, ví dụ: được xây dựng mà không khóa ngay lập tức nhưng để xây dựng trình bao bọc RAII (xem here).

std::lock_guard cũng cung cấp trình bao bọc RAII thuận tiện, nhưng không thể khóa nhiều mutex một cách an toàn. Nó có thể được sử dụng khi bạn cần một wrapper cho một phạm vi hạn chế, ví dụ .: Phải có chức năng thành viên:

class MyClass{ 
    std::mutex my_mutex; 
    void member_foo() { 
     std::lock_guard<mutex_type> lock(this->my_mutex);    
     /* 
     block of code which needs mutual exclusion (e.g. open the same 
     file in multiple threads). 
     */ 

     //mutex is automatically released when lock goes out of scope   
}; 

Để làm rõ một câu hỏi bằng cách chmike, theo mặc định std::lock_guardstd::unique_lock đều giống nhau. Vì vậy, trong trường hợp trên, bạn có thể thay thế std::lock_guard bằng std::unique_lock. Tuy nhiên, std::unique_lock có thể có thêm một chút chi phí.

+1

Với lệnh std :: unique_lock lock (myMutex); mutex có bị khóa bởi hàm tạo không? – chmike

+2

@chmike Có, nó sẽ. Đã thêm một số làm rõ. – inf

+0

Nếu tôi đang sử dụng trường hợp A, nó sẽ hiệu quả hơn khi sử dụng lock_gard thay vì unique_lock? – chmike

31

Sử dụng lock_guard trừ khi bạn cần để có thể tự unlock mutex ở giữa mà không phá hủy lock.

Cụ thể, condition_variable mở khóa mutex khi đi ngủ khi có cuộc gọi đến wait. Đó là lý do tại sao một số lock_guard không đủ ở đây.

+0

Việc chuyển lock_guard sang một trong các phương thức chờ của biến có điều kiện sẽ là tốt vì mutex luôn được phản hồi khi thời gian chờ kết thúc vì bất kỳ lý do gì. Tuy nhiên, tiêu chuẩn chỉ cung cấp một giao diện cho unique_lock. Điều này có thể được coi là thiếu trong tiêu chuẩn. –

+2

@Chris Bạn vẫn muốn ngắt đóng gói trong trường hợp này. Phương thức chờ sẽ cần để có thể giải nén mutex từ 'lock_guard' và mở khóa nó, do đó tạm thời phá vỡ sự bất biến của lớp bảo vệ. Mặc dù điều này xảy ra vô hình đối với người dùng, tôi sẽ xem xét rằng một lý do chính đáng cho việc không cho phép sử dụng 'lock_guard' trong trường hợp này. – ComicSansMS

+0

Nếu vậy, nó sẽ là vô hình và không thể phát hiện. gcc-4.8 thực hiện nó. wait (unique_lock &) gọi __gthread_cond_wait (& _ M_cond, __lock.mutex() -> native_handle()) (xem libstdC++ - v3/src/C++ 11/condition_variable.cc), gọi pthread_cond_wait() (xem libgcc/gthr) -posix.h). Điều tương tự cũng có thể được thực hiện đối với lock_guard (nhưng không phải vì nó không nằm trong tiêu chuẩn cho điều kiện_variable). –

73

lock_guardunique_lock là điều tương tự; lock_guard là phiên bản giới hạn với giao diện giới hạn.

A lock_guard luôn giữ một khóa từ việc xây dựng để phá hủy. Một unique_lock có thể được tạo mà không cần khóa ngay lập tức, có thể mở khóa bất kỳ lúc nào trong sự tồn tại của nó và có thể chuyển quyền sở hữu khóa từ phiên bản này sang phiên bản khác.

Vì vậy, bạn luôn sử dụng lock_guard, trừ khi bạn cần khả năng của unique_lock. A condition_variable cần số unique_lock.

+4

'Một điều kiện_variable cần một unique_lock.' - yes _but_ chỉ trên' wait() 'ing bên, như được xây dựng trong của tôi bình luận đến inf. –

-6

Như đã được đề cập bởi những người khác, std :: unique_lock theo dõi trạng thái bị khóa của mutex, vì vậy bạn có thể trì hoãn khóa cho đến sau khi khóa khóa và mở khóa trước khi phá khóa. std :: lock_guard không cho phép điều này.Có vẻ như không có lý do tại sao hàm std :: condition_variable wait không nên dùng một lock_guard cũng như unique_lock, bởi vì bất cứ khi nào chờ đợi kết thúc (vì bất kỳ lý do gì) mutex được tự động phản ứng lại để không gây ra bất kỳ vi phạm ngữ nghĩa nào . Tuy nhiên theo tiêu chuẩn, để sử dụng một std :: lock_guard với một biến điều kiện, bạn phải sử dụng một std :: condition_variable_any thay vì std :: condition_variable.

Chỉnh sửa: đã xóa "Sử dụng giao diện pthreads std :: condition_variable và std :: condition_variable_any phải giống hệt nhau". Trên nhìn vào thực hiện gcc của:

  • std :: condition_variable :: wait (std :: unique_lock &) chỉ gọi pthread_cond_wait() trên biến điều kiện pthread tiềm ẩn đối với các mutex được tổ chức bởi unique_lock (với và do đó có thể không kém làm tương tự cho lock_guard, nhưng không phải vì tiêu chuẩn không cung cấp cho điều đó)
  • std :: condition_variable_any có thể hoạt động với bất kỳ đối tượng có thể khóa nào, bao gồm cả khóa không phải là khóa mutex. với semaphore liên quá trình)
7

Có một số thông dụng nhất định giữa lock_guardunique_lock và một số khác biệt nhất định. Tuy nhiên, trong bối cảnh của câu hỏi được hỏi, trình biên dịch không cho phép sử dụng lock_guard kết hợp với biến điều kiện, bởi vì khi một chuỗi gọi chờ trên biến điều kiện, mutex sẽ tự động mở khóa và khi chuỗi/chủ đề khác thông báo và chuỗi hiện tại được gọi (không được chờ), khóa được mua lại.

Hiện tượng này trái với nguyên tắc lock_guard. lock_guard chỉ có thể được xây dựng một lần và bị hủy chỉ một lần.

Do đó, không thể sử dụng lock_guard kết hợp với biến điều kiện, nhưng có thể unique_lock (vì unique_lock có thể bị khóa và mở khóa nhiều lần).

+4

'trình biên dịch không cho phép sử dụng lock_guard kết hợp với biến điều kiện' Đây là sai. Nó chắc chắn ** không ** cho phép và làm việc hoàn hảo với 'lock_guard' trên' ing() 'ing bên. Chỉ có 'wait()' int bên yêu cầu 'unique_lock', vì' wait() 'phải giải phóng khóa trong khi kiểm tra điều kiện. –

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