2016-09-09 15 views
18

Tại sao std::lock_guardstd::unique_lock yêu cầu chỉ định loại khóa làm thông số mẫu?Tại sao không std :: lock_guard/std :: unique_lock sử dụng loại xóa?

Cân nhắc giải pháp thay thế sau. Thứ nhất, trong một không gian tên detail, có các lớp học kiểu tẩy xoá (một tổ chức phi mẫu lớp cơ sở trừu tượng, và một nguồn gốc lớp mẫu):

#include <type_traits> 
#include <mutex> 
#include <chrono> 
#include <iostream> 

namespace detail { 

    struct locker_unlocker_base { 
     virtual void lock() = 0; 
     virtual void unlock() = 0; 
    }; 

    template<class Mutex> 
    struct locker_unlocker : public locker_unlocker_base { 
     locker_unlocker(Mutex &m) : m_m{&m} {} 
     virtual void lock() { m_m->lock(); } 
     virtual void unlock() { m_m->unlock(); } 
     Mutex *m_m; 
    }; 
} 

Bây giờ te_lock_guard, khóa bảo vệ loại tẩy xoá, chỉ đơn giản là theo vị trí tin tức một đối tượng loại chính xác khi xây dựng (không phân bổ bộ nhớ động):

class te_lock_guard { 
public: 
    template<class Mutex> 
    te_lock_guard(Mutex &m) { 
     new (&m_buf) detail::locker_unlocker<Mutex>(m); 
     reinterpret_cast<detail::locker_unlocker_base *>(&m_buf)->lock(); 
    } 
    ~te_lock_guard() { 
     reinterpret_cast<detail::locker_unlocker_base *>(&m_buf)->unlock(); 
    } 

private: 
    std::aligned_storage<sizeof(detail::locker_unlocker<std::mutex>), alignof(detail::locker_unlocker<std::mutex>)>::type m_buf; 
}; 

tôi đã kiểm tra việc thực hiện so với các lớp học thư viện chuẩn của:

int main() { 
    constexpr std::size_t num{999999}; 
    { 
     std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 
     for(size_t i = 0; i < num; ++i) { 
      std::mutex m; 
      te_lock_guard l(m); 
     } 
     std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now(); 
     std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl; 
    } 
    { 
     std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 
     for(size_t i = 0; i < num; ++i) { 
      std::mutex m; 
      std::unique_lock<std::mutex> l(m); 
     } 
     std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now(); 
     std::cout << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl; 
    } 
} 

Sử dụng g ++ với -O3, không có tổn thất hiệu năng đáng kể về mặt thống kê.

+5

'có ý nghĩa thống kê' đối với bạn là gì? Việc thực hiện bản địa có vẻ nhanh hơn sau khi chạy nó 4-5 lần. Vì vậy, không chắc chắn những gì bạn thấy là không đáng kể. Và tại sao chọn triển khai mặc định chậm hơn so với giải pháp thay thế hiện tại? Câu hỏi chính nó là khá interessting mặc dù. Một người nào đó đã đo một giải pháp thay thế. – Hayt

+3

Nếu bạn thực sự quá thất vọng vì phải nhập lại tên loại, bạn có thể sử dụng mẫu hàm: 'auto && lock = guard_me (mx);' ([Demo] (https://ideone.com/ysxJEz) .) –

+0

@Hayt Điều tôi muốn nói là một bài kiểm tra hoán vị thống kê đã không bác bỏ giả thuyết rằng chúng đến từ cùng một phân bố. Theo tôi, sự thay thế phức tạp thực sự đôi khi nhanh hơn, tình cờ. Xử lý số liệu thống kê, tôi cố gắng tránh những tuyên bố mạnh mẽ như "chúng chạy cùng tốc độ". –

Trả lời

25

Bởi vì điều này làm phức tạp việc triển khai không mang lại lợi ích đáng kể nào, và giấu thực tế là std::lock_guardstd::unique_lock nhận thức được loại khóa mà chúng đang bảo vệ lúc biên dịch.

Giải pháp của bạn là giải pháp cho việc loại trừ tham số mẫu lớp không xảy ra trong khi xây dựng - điều này được giải quyết trong tiêu chuẩn sắp tới.

Cần thiết để chỉ định loại khóa là bản mẫu khó chịu sẽ được giải quyết trong C++ 17 (không chỉ cho khóa bảo vệ) nhờ đề xuất Template parameter deduction for constructors (P0091R3).

Đề xuất (được chấp nhận), cho phép các thông số mẫu được rút ra từ nhà thầu, loại bỏ nhu cầu make_xxx(...) chức năng helper hoặc chỉ định một cách rõ ràng typenames rằng trình biên dịch sẽ có thể suy ra:

// Valid C++17 
for(size_t i = 0; i < num; ++i) { 
    std::mutex m; 
    std::unique_lock l(m); 
} 
+0

Điều đó rất thú vị. Cảm ơn các liên kết. –

+0

Tôi tin rằng bản sửa đổi này được diễn đạt tốt hơn và được sửa chữa ở một số nơi (typos, v.v.) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r2.html. Mặc dù tôi không thể tìm thấy thông tin mà trên đó thực tế được chấp nhận. – Patryk

+1

@Patryk: cảm ơn, p0091r3 là trang được chấp nhận [theo trang này] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/#mailing2016-07) –

7

Cuộn trên C++17 ... Trong thời gian chờ đợi, không cần loại bỏ. Chiết khấu đối số chức năng mẫu cho phép chúng tôi trợ giúp dễ dàng:

template<class Mutex> 
auto make_lock(Mutex& m) 
{ 
    return std::unique_lock<Mutex>(m); 
} 

... 

std::mutex m; 
std::recursive_mutex m2; 

auto lock = make_lock(m); 
auto lock2 = make_lock(m2); 
+0

Tùy chọn tốt , cảm ơn. –

+0

Vâng, và đó là tùy chọn mà mọi người đã sử dụng cho đến khi C++ 17, bao gồm cả bản thân stdlib, vì vậy tôi ngạc nhiên khi bạn chưa từng thấy nó trước đây. : P –

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