一、std::lock_guard类
1.lock_guard是一个模板类, 用于管理互斥量的加锁和解锁
在需要加锁的代码块中创建一个lock_guard对象(调用构造函数), 在lock_guard对象的生命周期结束时(调用析构函数), 会自动解锁
lock_guard禁用拷贝构造函数和赋值运算符,无法复制和移动
常用构造函数:
1
| explicit lock_guard( mutex_type& m );
|
以一个互斥量作为参数。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream> #include <thread> #include <mutex>
using std::cin; using std::cout;
std::mutex mtx; int shared_data = 0; void func() { for (int i = 0; i < 1000000; i++) { std::lock_guard<std::mutex> lock(mtx); shared_data++; } }
int main() { std::ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
std::thread t1(func), t2(func); t1.join(), t2.join(); cout << shared_data << '\n'; return 0; }
|
程序正常输出2000000,并且多次运行,不会出现冲突情况。
附:
类 lock_guard 是互斥体包装器,为在作用域块期间占有互斥体提供便利的 RAII 风格机制。
当创建 lock_guard 对象时,它尝试接收给定互斥体的所有权。当控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥体。
lock_guard 类不可复制。
一种常见的新手错误是“忘记”给 lock_guard 变量命名,例如 std::lock_guard(mtx);(它默认构造了一个名为 mtx 的 lock_guard 变量,或者 std::lock_guard{mtx};(它构造
了一个纯右值对象并立即销毁),而并未真正为作用域的剩余部分构造持有互斥体的锁。
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| template <typename _Mutex> class lock_guard { public: typedef _Mutex mutex_type;
[[__nodiscard__]] explicit lock_guard(mutex_type &__m) : _M_device(__m) { _M_device.lock(); }
[[__nodiscard__]] lock_guard(mutex_type &__m, adopt_lock_t) noexcept : _M_device(__m) { }
~lock_guard() { _M_device.unlock(); }
lock_guard(const lock_guard &) = delete; lock_guard &operator=(const lock_guard &) = delete;
private: mutex_type &_M_device; };
|
二、std::unique_lock类
相较于lock_guard,标准库为我们提供了一个更强大的工具:unique_lock类:
1.std::unique_lock是一个模板类, 用于管理互斥量的加锁和解锁。
2.std::unique_lock提供了更多的灵活性, 可以在任何时候手动加锁和解锁。
3.std::unique_lock可以被复制和移动。
4.std::unique_lock可以在构造函数中指定是否加锁。
5.部分成员函数lock(),try_lock(),try_lock_for(),try_lock_until(),unlock()。
lock_guard()是std::unique_lock的一个特例,缺点也显而易见:会占用更多的资源
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
| template <typename _Mutex> class unique_lock { public: typedef _Mutex mutex_type;
unique_lock() noexcept : _M_device(0), _M_owns(false) { }
[[__nodiscard__]] explicit unique_lock(mutex_type &__m) : _M_device(std::__addressof(__m)), _M_owns(false) { lock(); _M_owns = true; }
unique_lock(mutex_type &__m, defer_lock_t) noexcept : _M_device(std::__addressof(__m)), _M_owns(false) { }
[[__nodiscard__]] unique_lock(mutex_type &__m, try_to_lock_t) : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock()) { }
[[__nodiscard__]] unique_lock(mutex_type &__m, adopt_lock_t) noexcept : _M_device(std::__addressof(__m)), _M_owns(true) { }
template <typename _Clock, typename _Duration> [[__nodiscard__]] unique_lock(mutex_type &__m, const chrono::time_point<_Clock, _Duration> &__atime) : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock_until(__atime)) { }
template <typename _Rep, typename _Period> [[__nodiscard__]] unique_lock(mutex_type &__m, const chrono::duration<_Rep, _Period> &__rtime) : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock_for(__rtime)) { }
~unique_lock() { if (_M_owns) unlock(); }
unique_lock(const unique_lock &) = delete; unique_lock &operator=(const unique_lock &) = delete;
unique_lock(unique_lock &&__u) noexcept : _M_device(__u._M_device), _M_owns(__u._M_owns) { __u._M_device = 0; __u._M_owns = false; }
unique_lock &operator=(unique_lock &&__u) noexcept { if (_M_owns) unlock();
unique_lock(std::move(__u)).swap(*this);
__u._M_device = 0; __u._M_owns = false;
return *this; }
void lock() { if (!_M_device) __throw_system_error(int(errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int(errc::resource_deadlock_would_occur)); else { _M_device->lock(); _M_owns = true; } }
_GLIBCXX_NODISCARD bool try_lock() { if (!_M_device) __throw_system_error(int(errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int(errc::resource_deadlock_would_occur)); else { _M_owns = _M_device->try_lock(); return _M_owns; } }
template <typename _Clock, typename _Duration> _GLIBCXX_NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration> &__atime) { if (!_M_device) __throw_system_error(int(errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int(errc::resource_deadlock_would_occur)); else { _M_owns = _M_device->try_lock_until(__atime); return _M_owns; } }
template <typename _Rep, typename _Period> _GLIBCXX_NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period> &__rtime) { if (!_M_device) __throw_system_error(int(errc::operation_not_permitted)); else if (_M_owns) __throw_system_error(int(errc::resource_deadlock_would_occur)); else { _M_owns = _M_device->try_lock_for(__rtime); return _M_owns; } }
void unlock() { if (!_M_owns) __throw_system_error(int(errc::operation_not_permitted)); else if (_M_device) { _M_device->unlock(); _M_owns = false; } }
void swap(unique_lock &__u) noexcept { std::swap(_M_device, __u._M_device); std::swap(_M_owns, __u._M_owns); }
mutex_type * release() noexcept { mutex_type *__ret = _M_device; _M_device = 0; _M_owns = false; return __ret; }
_GLIBCXX_NODISCARD bool owns_lock() const noexcept { return _M_owns; }
explicit operator bool() const noexcept { return owns_lock(); }
_GLIBCXX_NODISCARD mutex_type * mutex() const noexcept { return _M_device; }
private: mutex_type *_M_device; bool _M_owns; };
|
我们选几个典型来熟悉它的用法:
std::defer_lock
传入std::defer_lock,可以选择性延迟加锁:
1 2
| unique_lock(mutex_type &__m, defer_lock_t) noexcept : _M_device(std::__addressof(__m)), _M_owns(false) {}
|
try_lock_for()
尝试加锁,如果遇到mtx上锁的情况,可以选择性等待指定的时间,如果当前mtx可以加锁,就会给它加锁;若时间到后仍没有解锁,就会返回 false,并且跳过。
注:若使用该函数,mtx需使用std::timed_mutex时间锁。
1 2 3
| std::timed_mutex t_mtx; std::unique_lock<std::timed_mutex> u_lock(t_mtx, std::defer_lock); u_lock.try_lock_for(std::chrono::milliseconds(1000));
|
std::swap
特化的std::swap可对两个unique_lock进行交换。
复制构造,赋值禁用,但移动构造可用
1 2 3 4 5 6 7 8
| unique_lock &operator=(unique_lock &&__u) noexcept { if (_M_owns) unlock(); __u._M_device = 0; __u._M_owns = false; unique_lock(std::move(__u)).swap(*this); }
|