std :: lock_guard hoặc std :: scoped_lock?


143

C ++ 17 đã giới thiệu một lớp khóa mới được gọi là std::scoped_lock.

Đánh giá từ tài liệu này có vẻ tương tự như std::lock_guardlớp đã có .

Sự khác biệt là gì và khi nào tôi nên sử dụng nó?

Câu trả lời:


125

Đây scoped_locklà một phiên bản ưu việt hoàn toàn lock_guard, khóa một số lượng các trường hợp tùy ý cùng một lúc (sử dụng cùng một thuật toán tránh bế tắc như std::lock). Trong mã mới, bạn chỉ nên sử dụng scoped_lock.

Lý do duy nhất lock_guardvẫn tồn tại là để tương thích. Nó không thể bị xóa, bởi vì nó được sử dụng trong mã hiện tại. Hơn nữa, nó đã tỏ ra không mong muốn thay đổi định nghĩa của nó (từ unary sang matrixdic), bởi vì đó cũng là một điều có thể quan sát được, và do đó phá vỡ, thay đổi (nhưng vì lý do kỹ thuật nào đó).


8
Ngoài ra, nhờ vào việc khấu trừ đối số mẫu lớp, bạn thậm chí không phải liệt kê ra các loại có thể khóa.
Nicol Bolas

3
@NicolBolas: Điều đó đúng, nhưng điều đó cũng đúng với lock_guard. Nhưng nó chắc chắn làm cho các lớp bảo vệ dễ sử dụng hơn một chút.
Kerrek SB

6
scoped_lock chỉ là C ++ 17
Shital Shah

1
Vì nó là c ++ 17, khả năng tương thích là một lý do đặc biệt tốt cho sự tồn tại của nó. Tôi cũng không đồng ý với bất kỳ tuyên bố tuyệt đối nào về "bạn chỉ nên sử dụng" khi mực vẫn còn khô từ tiêu chuẩn này.
Paul Childs

88

Sự khác biệt duy nhất và quan trọng là std::scoped_lockcó một hàm tạo dao động lấy nhiều hơn một mutex. Điều này cho phép khóa nhiều mutexes trong một cách tránh bế tắc như thể std::lockđược sử dụng.

{
    // safely locked as if using std::lock
    std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);     
}

Trước đây bạn phải thực hiện một điệu nhảy nhỏ để khóa nhiều đột biến một cách an toàn bằng cách std::lockgiải thích câu trả lời này .

Việc bổ sung khóa phạm vi giúp việc này dễ sử dụng hơn và tránh các lỗi liên quan. Bạn có thể xem xét std::lock_guardkhông dùng nữa. Trường hợp đối số duy nhất std::scoped_lockcó thể được triển khai như một chuyên môn và do đó bạn không phải lo lắng về các vấn đề hiệu suất có thể xảy ra.

GCC 7 đã có hỗ trợ std::scoped_lockcó thể được nhìn thấy ở đây .

Để biết thêm thông tin bạn có thể muốn đọc giấy tiêu chuẩn


9
Trả lời câu hỏi của riêng bạn chỉ sau 10 phút. Bạn có thực sự không biết?
Walter

23
@Walter Tôi đã làm stackoverflow.blog/2011/07/01/
Kiếm

3
Khi tôi đưa nó lên ủy ban, câu trả lời là "không có gì." Nó có thể là trường hợp suy biến của một số thuật toán, đây là chính xác điều đúng. Hoặc có thể là đủ người vô tình khóa không có gì khi họ có ý định khóa một cái gì đó là một vấn đề phổ biến. Tôi thực sự không chắc chắn.
Howard Hinnant

3
@HowardHinnant : scoped_lock lk; // locks all mutexes in scope. LGTM.
Kerrek SB

2
@KerrekSB: scoped_lock lk;là cách viết tắt mới cho scoped_lock<> lk;. Có không có mutexes. Vì vậy, bạn đã đúng. ;-)
Howard Hinnant

26

Câu trả lời muộn và chủ yếu là để đáp lại:

Bạn có thể xem xét std::lock_guardkhông dùng nữa.

Đối với trường hợp phổ biến là người ta cần khóa chính xác một mutex, std::lock_guardcó API an toàn hơn một chút để sử dụng scoped_lock.

Ví dụ:

{
   std::scoped_lock lock; // protect this block
   ...
}

Đoạn mã trên có thể là một lỗi thời gian chạy ngẫu nhiên vì nó biên dịch và sau đó hoàn toàn không làm gì cả. Các lập trình viên có thể có nghĩa là:

{
   std::scoped_lock lock{mut}; // protect this block
   ...
}

Bây giờ nó khóa / mở khóa mut.

Nếu lock_guardđược sử dụng trong hai ví dụ trên thay vào đó, ví dụ đầu tiên là lỗi thời gian biên dịch thay vì lỗi thời gian chạy và ví dụ thứ hai có chức năng giống hệt như phiên bản sử dụng scoped_lock.

Vì vậy, lời khuyên của tôi là sử dụng công cụ đơn giản nhất cho công việc:

  1. lock_guard nếu bạn cần khóa chính xác 1 mutex cho toàn bộ phạm vi.

  2. scoped_lock nếu bạn cần khóa một số mutexes không chính xác 1.

  3. unique_locknếu bạn cần mở khóa trong phạm vi của khối (bao gồm sử dụng với a condition_variable).

Lời khuyên này không ngụ ý rằng scoped_locknên được thiết kế lại để không chấp nhận 0 mutexes. Có tồn tại các trường hợp sử dụng hợp lệ trong đó mong muốn scoped_lockchấp nhận các gói tham số mẫu có thể bị trống. Và trường hợp trống không nên khóa bất cứ điều gì.

Và đó là lý do tại sao lock_guardkhông được tán thành. scoped_lock unique_lock có thể là một siêu chức năng của lock_guard, nhưng thực tế đó là con dao hai lưỡi. Đôi khi nó cũng quan trọng như những gì một loại sẽ không làm (cấu trúc mặc định trong trường hợp này).


13

Dưới đây là một mẫu và trích dẫn từ C ++ đồng thời trong hành động :

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == & rhs)
        return;
    std::lock(lhs.m, rhs.m);
    std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
    std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
    swap(lhs.some_detail, rhs.some_detail);
}

so với

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == &rhs)
        return;
    std::scoped_lock guard(lhs.m, rhs.m);
    swap(lhs.some_detail, rhs.some_detail);
}

Sự tồn tại của std::scoped_lockphương tiện có nghĩa là hầu hết các trường hợp mà bạn đã sử dụng std::locktrước c ++ 17 bây giờ có thể được viết bằng cách sử dụng std::scoped_lock, với ít khả năng xảy ra lỗi hơn, chỉ có thể là một điều tốt!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.