一、定义
C++11中提供了std::mutex互斥量,共包含四种类型:
- std::mutex:最基本的mutex类。
- std::recursive_mutex:递归mutex类,能多次锁定而不死锁。
- std::time_mutex:定时mutex类,可以锁定一定的时间。
- std::recursive_timed_mutex:定时递归mutex类。
另外,还提供了两种锁类型:
- std::lock_guard:方便线程对互斥量上锁。
- std::unique_lock:方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。
相关函数
- std::try_lock:尝试同时对多个互斥量上锁。
- std::lock:可以同时对多个互斥量上锁。
- std::call_once:如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次。
std::lock_guard自动释放锁,它是一个模板类,模板类型可以是以上的四种锁,用于自动锁定解锁,直到对象作用域结束。其原理是:声明一个局部的lock_guard对象,在其构造函数中进行加锁,在其析构函数中进行解锁。最终的结果就是:在定义该局部对象的时候加锁(调用构造函数),出了该对象作用域的时候解锁(调用析构函数)
二、实例
使用方法
1.首先需要包含mutex头文件
2.然后创建一个锁 std::mutex mutex
3.在需要被加锁的作用域内 将mutex传入到创建的std::lock_guard局部对象中
举个例子
std::mutex mtx;
int main()
{
{
std::lock_guard<std::mutex> guard(mtx);
std::cout << "互斥量被占用"<< mtx.try_lock() << std::endl; // 0,互斥量被占用
}
std::cout <<"互斥量被释放" <<mtx.try_lock() << std::endl; // 1,互斥量释放
mtx.unlock();
return 0;
}
std::try_lock支持尝试对多个互斥量进行锁定,尝试锁定成功返回-1,否则返回锁定失败的互斥量的位置,例如第一个锁定失败返回0、第二个失败返回1。
int main ()
{
std::mutex mtx1;
std::mutex mtx2;
if (-1 == std::try_lock(mtx1, mtx2))
{
std::cout << "locked" << std::endl;
mtx1.unlock();
mtx2.unlock();
}
return 0;
}
三、避免死锁
假设有两个mutex(m1、m2),一个线程先锁住m1再锁住m2,另一个线程先锁住m2,再锁住m1,就有可能会出现死锁。std::lock内部使用了死锁避免的算法,可以有效避免死锁
std::lock(m1, m2)
std::lock_guard lock1(m1, std::adopt_lock)
std::lock_guard lock2(m2, std::adopt_lock)
std::lock支持对多个锁锁定,并且避免死锁的出现,以下代码运行时有可能出现死锁的情况:
void func(std::mutex* mtx1, std::mutex* mtx2, int index)
{
std::lock_guard<std::mutex> lock1(*mtx1,std::adopt_lock);
std::lock_guard<std::mutex> lock2(*mtx2,std::adopt_lock);
std::cout << index << "out\n";
}
int main ()
{
std::mutex mtx1;
std::mutex mtx2;
// 两个线程的互斥量锁定顺序不同,可能造成死锁
std::thread t1(func, &mtx1, &mtx2, 1);
std::thread t2(func, &mtx2, &mtx1, 2);
t1.join();
t2.join();
return 0;
}
而用std::lock能避免多个锁出现死锁
void func(std::mutex* mtx1, std::mutex* mtx2, int index)
{
// std::lock_guard只是保证互斥量在作用域结束时被释放
std::lock(*mtx1, *mtx2); // 同时锁定
std::lock_guard<std::mutex> lock1(*mtx1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(*mtx2, std::adopt_lock);
// 等价方法:
/*std::unique_lock<std::mutex> lock1(*mtx1, std::defer_lock);
std::unique_lock<std::mutex> lock2(*mtx2, std::defer_lock);
std::lock(lock1, lock2);*/
std::cout << index << "out\n";
}
int main()
{
std::mutex mtx1;
std::mutex mtx2;
std::thread t1(func, &mtx1, &mtx2, 1);
std::thread t2(func, &mtx2, &mtx1, 2);
t1.join();
t2.join();
return 0;
}
三个窗口卖100张票
std::mutex mtx; // 全局的一把互斥锁
int ticketCount = 100;;
void sellTicket(int index)
{
while (ticketCount > 0) // ticketCount=1 锁+双重判断
{
// 保证所有线程都能释放锁,防止死锁问题的发生 scoped_ptr
lock_guard<std::mutex> lock(mtx);
if (ticketCount > 0)
{
// 临界区代码段 =》 原子操作 =》 线程间互斥操作了 =》 mutex
cout << "窗口:" << index << "卖出第:" << ticketCount << "张票!" << endl;
//cout << ticketCount << endl;
ticketCount--;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main()
{
list<std::thread> tlist;
for (int i = 0; i < 3; ++i)
{
tlist.push_back(std::thread(sellTicket, i));
}
for (std::thread &t : tlist)
{
t.join();
}
cout << "所有窗口卖票结束!" << endl;
}
四、unique_lock
简单地讲,unique_lock 是 lock_guard 的升级加强版,它具有 lock_guard 的所有功能,同时又具有其他很多方法,使用起来更强灵活方便,能够应对更复杂的锁定需要。
特点如下:
- 创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
- 可以随时加锁解锁
- 作用域规则同 lock_grard,析构时自动释放锁
- 不可复制,可移动
- 条件变量需要该类型的锁作为参数(此时必须使用unique_lock)
#include <mutex>
#include <thread>
#include <chrono>
struct Box {
explicit Box(int num) : num_things{num} {}
int num_things;
std::mutex m;
};
void transfer(Box &from, Box &to, int num)
{
// don't actually take the locks yet
std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
// lock both unique_locks without deadlock
std::lock(lock1, lock2);
from.num_things -= num;
to.num_things += num;
std::cout << from.num_things << " out\n";
std::cout << to.num_things << " out\n";
// 'from.m' and 'to.m' mutexes unlocked in 'unique_lock' dtors
}
int main()
{
Box acc1(100);
Box acc2(50);
std::thread t1(transfer, std::ref(acc1), std::ref(acc2), 10);
std::thread t2(transfer, std::ref(acc2), std::ref(acc1), 5);
t1.join();
t2.join();
}
参考:
《深入应用C++11》笔记-互斥量std::mutex、锁std::lock_guard_WizardtoH的博客-CSDN博客_mutex_lock
c++11中的lock_guard和unique_lock使用浅析_guotianqing的博客-CSDN博客_lock_guard和unique_lock