C++11 线程库—互斥锁

前言

多线程因其调度的随机性和时间片分配,如果没有限制的访问临界资源,会导致出现无法预测的结果,也无法达到预期。
所以,访问临界区,需要是原子性的,在一个线程完成之前,不能有其他线程访问,影响。
互斥量的底层原理可以参看[Linux]线程互斥

文章目录

  • 前言
  • 一. mutex
    • 1. 构造函数
    • 2. 加锁与解锁
  • 二. recursive_mutex
  • 三. timed_mutex
  • 四. lock_guard和unique_lock
    • 1. 构造函数
  • 结束语

C++11 线程库—互斥锁_第1张图片

在C++11的线程库中,有很多适用于不同场景的互斥量

C++11 线程库—互斥锁_第2张图片

一. mutex

mutex是互斥锁的意思,其成员函数如下:
C++11 线程库—互斥锁_第3张图片

1. 构造函数

C++11 线程库—互斥锁_第4张图片

函数声明 说明
constexpr mutex() noexcept 不会抛异常的无参构造
mutex(const mutex&) = delete 不支持拷贝构造

PS:
noexcept 在函数声明后作标识符,默认是noexcept(true),表示不会抛异常
constexpr:让该函数在编译时生成,而不是运行时
=delete 在函数声明后,表示不会生成该函数

2. 加锁与解锁

C++11线程库其实就是对系统调用的封装,将其封装成一个类

C++11 线程库—互斥锁_第5张图片

函数声明 说明
void lock() 加锁
bool try_lock() 尝试加锁
void unlock() 解锁

着重讲解一下try_lock

try_lock分为3种情况

  1. 没有线程持有锁,则调用try_lock的线程获得锁
  2. 其他线程持有锁,则加锁失败,返回false
  3. 当前线程持有锁,不进行操作

二. recursive_mutex

关于死锁的概念,可以参看Linux死锁

如果我们在递归中使用互斥锁,就会出现死锁的情况

mutex _mutex;

void Func(int n)
{
	if (n == 0)
	{
		return;
	}

	_mutex.lock();
	cout<<n<<endl;
	Func(n-1);

	_mutex.unlock();
}

int main()
{
	thread t1(Func, 7);
	t1.join();

	return 0;
}

C++11 线程库—互斥锁_第6张图片

为了解决这一问题,C++11提供了递归互斥锁 recursive_mutex

C++11 线程库—互斥锁_第7张图片

基本用法同mutex

三. timed_mutex

timed_mutex是定时互斥锁

C++11 线程库—互斥锁_第8张图片

多提供了try_lock_for和try_lock_until

try_lock_for:加锁情况同try_lock,但是支持加锁一段时间
try_lock_until:支持加锁到一个时间点

四. lock_guard和unique_lock

手动的加锁与解锁难免有些麻烦,于是C++11根据RAII习语管理资源,lock_guard在构造函数中自动绑定构造互斥锁,并且加锁,大大减少了死锁的风险,并且在析构函数中调用解锁,避免了忘记解锁等不必要的麻烦。

1. 构造函数

C++11 线程库—互斥锁_第9张图片

函数声明 说明
explicit lock_guard(mutex_type&m) 构造函数,需要传一把锁
lock_guard(mutex_type& m,adopt_lock tag) 将锁转移到lock_guard的锁
lock_guard(const lock_guard&) = delete 不支持拷贝构造

adopt_lock的作用正如他的命名,寄养锁,使用如下:

std::mutex _mutex;
void test5() {//std::adopt_mutex的大妙处
	_mutex.lock();
	lock_guard<std::mutex> lg(_mutex, std::adopt_lock);
	//在这里进行收养锁
	cout << "hello test5" << endl;
}

这样就将_mutex的锁转移到lock_guard中,由lock_guard管理

但是lock_guard只会在构造时加锁,析构时解锁,如果途中我们有解锁和需求则无法完成,所以unique_lock出现了。


unique_lock同样也遵守RAII习语管理资源,构造时加锁,析构时解锁,但是unique_lock还支持手动加锁和解锁

C++11 线程库—互斥锁_第10张图片

  • 赋值重载operator=的使用同thread的operator=,可以使用匿名对象创建。
  • 调用release会释放其管理的锁

结束语

感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

你可能感兴趣的:(C++学习笔记,c++,开发语言,算法)