在 C++1x 之后,我们编写多线程可以直接使用标准库里的函数,不必根据平台的不同使用 posix_thread 之类的库了,这样就实现了跨平台的编程。
一、std::lock_guard 的介绍
std::lock_guard 的原型是一个模板类,定义如下:
template<class Mutex> class lock_guard;
lock_guard 通常用来管理一个 std::mutex 类型的对象,通过定义一个 lock_guard 一个对象来管理 std::mutex 的上锁和解锁。在 lock_guard 初始化的时候进行上锁,然后在 lock_guard 析构的时候进行解锁。这样避免了我们对 std::mutex 的上锁和解锁的管理。注意,lock_guard 并不管理 std::mutex 对象的声明周期,也就是说在使用 lock_guard 的过程中,如果 std::mutex 的对象被释放了,那么在 lock_guard 析构的时候进行解锁就会出现空指针错误之类。
下面是一个使用例子:
//
// Created by 陈国威 on 2018/6/21.
//
#include <iostream>
#include <thread>
#include <mutex>
#include <stdexcept>
std::mutex mtx;
using std::cout;
using std::endl;
void print_event(int x)
{
if (x % 2 == 0)
{
cout << x << "is event" << endl;
}
else
{
throw(std::logic_error("not event"));
}
}
void print_id(int id)
{
try
{
std::lock_guard<std::mutex> lck(mtx);
print_event (id);
}
catch(std::logic_error&)
{
cout << "[exception caught]\n";
}
}
int main()
{
std::thread threads[10];
for (int i = 0; i < 10; ++i)
{
threads[i] = std::thread(print_id, i+1);
}
for (auto &th : threads)
{
th.join ();
}
return 0;
}
二、std::unique_lock 的介绍
unique_lock 和 lock_guard 一样,对 std::mutex 类型的互斥量的上锁和解锁进行管理,一样也不管理 std::mutex 类型的互斥量的声明周期。但是它的使用更加的灵活,支持的构造函数如下:
default (1) | unique_lock() noexcept; |
---|---|
locking (2) | explicit unique_lock(mutex_type& m); |
try-locking (3) | unique_lock(mutex_type& m, try_to_lock_t tag); |
deferred (4) | unique_lock(mutex_type& m, defer_lock_t tag) noexcept; |
adopting (5) | unique_lock(mutex_type& m, adopt_lock_t tag); |
locking for (6) | template <class Rep, class Period> unique_lock(mutex_type& m, const chrono::duration<Rep,Period>& rel_time); |
locking until (7) | template <class Clock, class Duration> unique_lock(mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time); |
copy [deleted] (8) | unique_lock(const unique_lock&) = delete; |
move (9) | unique_lock(unique_lock&& x); |
(2) locking 初始化 新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞。 (3) try-locking 初始化 新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.try_lock() 对 Mutex 对象进行上锁,但如果上锁不成功,并不会阻塞当前线程。 (4) deferred 初始化 新创建的 unique_lock 对象管理 Mutex 对象 m,但是在初始化的时候并不锁住 Mutex 对象。 m 应该是一个没有当前线程锁住的 Mutex 对象。 (5) adopting 初始化 新创建的 unique_lock 对象管理 Mutex 对象 m, m 应该是一个已经被当前线程锁住的 Mutex 对象。(并且当前新创建的 unique_lock 对象拥有对锁(Lock)的所有权)。 (6) locking 一段时间(duration) 新创建的 unique_lock 对象管理 Mutex 对象 m,并试图通过调用 m.try_lock_for(rel_time) 来锁住 Mutex 对象一段时间(rel_time)。 (7) locking 直到某个时间点(time point) 新创建的 unique_lock 对象管理 Mutex 对象m,并试图通过调用 m.try_lock_until(abs_time) 来在某个时间点(abs_time)之前锁住 Mutex 对象。 (8) 拷贝构造 [被禁用] unique_lock 对象不能被拷贝构造。 (9) 移动(move)构造
新创建的 unique_lock 对象获得了由 x 所管理的 Mutex 对象的所有权(包括当前 Mutex 的状态)。调用 move 构 造之后, x 对象如同通过默认构造函数所创建的,就不再管理任何 Mutex 对象了。
三、总结
如果我们只是简单地使用互斥量的话,首选 lock_guard就可以了。如果要对 互斥量添加一些限制条件啥的,那么使用 unique_lock 更加地灵活。