c++11下的多线程与四种互斥量

 c++11引入了thread类以及其他用于多线程的类,使得我们可以在vs2013下开心的使用多线程,不仅如此,thread类的引入还极大的降低了编程本身的难度,代码简短移动,便于阅读。这里就对c++11下四种互斥量进行解释和简单的使用。首先,互斥量存在的意义。举个简单的例子,加入大家在画一幅画,而只有彩绘工具,如果只有一个人在画,虽然效率低一些但是肯定不会出现什么冲突的现象。如果我们想提高效率,好吧,我们雇5个人同时来写,效率理论上会提高,但是某工甲想用刷子了,结果刷子被某工乙拿去用了,这时乙不知道该等着还是会去工作,这就出现了冲突。这个问题体现在代码中,加入几个线程都可能会操作某静态全局变量num,如果不对大家进行协调,进程1在访问num的时候,进程2也跑来访问,问题就产生了,进程2的访问很可能就变得无效了。因此必须某个中立的家伙,协调大家工作,对于刚才进程1,2都来访问num的问题,这个中立方即可以规定进程2访问失败,直接终止访问,继续执行进程2剩余的部分;也可以规定进程2在此无上限等待进程1访问num结束,直至进程2成功访问num;也可以规定一个时间段,在此时间内进程2等待进程1访问完num则进程2访问num,超出时间则进程2停止访问,继续执行进程剩余的部分。显然,这么复杂的工作,必然有人帮我们完成了,那就是互斥量。互斥量,顾名思义,把大家的行为分开,在逻辑上是如何实现的呢?互斥量并不是绑定某个变量,类似于看门狗一样看着他,一次只能有一个进程进入其中进行访问,相反,它实现的逻辑更类似于多个红绿灯,同一个互斥量分布在程序的某些角落(类似于断点),对于同一个互斥量,一旦在某处被锁定,也就是亮了绿灯,则在此绿灯期间,其他所有的其他所有该互斥量都亮红灯,触发绿灯的进程可以继续执行,其他进程如果进行到有该红绿灯(同一互斥量)的位置,都将停止。显然对于上述进程1,进程2访问num的状况,只需要在每个访问num的语句前设置同一个互斥量即可,有(第)一个进程进行访问,则亮绿灯同行,其他该互斥量全部红灯,直到第一个进程访问完,其他进程才可以访问(具体情况看互斥量的类型和具体设置)。

下面来看看c++11给我们提供了哪些互斥量。互斥量一共有4种,是四个类:std::mutex 基本互斥量,std::recursive_mutex递归互斥量(可以重复上锁),std::time_mutex(可以规定某个时间段内尝试上锁),std::recursive_timed_mutex(集上二者大成,可以在某时间段内重复上锁)。 显然,上述几类都被定义在名称空间std中的普通类,不是类模板。

1.std::mutex 基本互斥量

std::mutex是std::_Mutex_base的派生类,其方法与之相同如下,成员函数:

void lock():当前线程拥有该互斥量,或者说锁住该互斥量。此时同一mutex类对象调用lock函数都将被拒绝,并且对于std::mutex类,不允许同一进程重复锁住互斥量。像下面的操作是不行滴,这会出现死锁的现象,直接报错。

#include<mutex>
std::mutex m1;
void fun()
{
	m1.lock();
	m1.lock();
	//operation
	...;
	m1.unlock();
	m1.unlock();
}
void unlock():解除进程对互斥量的锁定,与上面的lock()对应,很好理解,执行完操作后就解锁。

bool try_lock():尝试锁定,如果锁定成功,返回true,锁定失败,返回false。这样的设定意图就显而易见了,既可以利用if和try_lock()结合实现“尝试锁定一次,失败便继续执行”的逻辑,也可以用while和try_lock的结合实现“始终尝试锁定,直至成功”的逻辑。代码如下:

#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
static int count_ = 0;
std::mutex mtx;
void fun()
{
	for (int i = 0; i < 10000; i++)
	{
		if (mtx.try_lock())
		{
		count_++;
		mtx.unlock();
		}
	}
}
int main()
{
	std::thread threads[10];
	for (auto& i : threads)
	{
		i = std::thread(fun);
	}
	for (auto& i : threads)
	{
		i.join();
	}
	std::cout << "count:" << count_ << std::endl;
}

显然,上面的代码中十个进程都尝试修改同一个静态全局变量,每个进程重复一万次循环,每次循环+1,看起来最后的结果应该是十万,但实际结果如下:

原因很简单,每次上锁我们都用的是try_lock(),上锁失败也不会在这等着,直接跳出if语句,结束本次循环。所以最后的结果差不多也就是一个进程需改后的count_的值,多一些是因为偶尔在没有被锁定的时候,其他进程穿插进来偶尔加1。如果想出现十万的结果,只需要把fun函数的循环体做一下小小的修改如下:

void fun()
{
	for (int i = 0; i < 10000; i++)
	{
		mtx.lock();
		count_++;
		mtx.unlock();
	}
}
这样就可以正确的现实结果了,因为上锁失败后,进程会一直等在互斥量上锁的这个位置,直到锁被其他位置的互斥量打开,上锁成功,进程才会继续。结果如下:

2.std::recursive_mutex 递归互斥量

这个递归互斥量的特点就是,允许同一进程多次对某互斥量重复上锁,而不会出现死锁。但是上锁次数和开锁次数必须吻合,线程才会真正的解锁这个互斥量。这就类似于递归的调用某函数,进入函数退出函数的次数必须相同,入栈出栈的次数也必须相同,才会回到最初的状态,因此美名曰递归互斥量(个人理解,欢迎拍砖)。

这个类的方法和基本互斥量是一样的,唯一的区别就在于可以重复上锁,下面举一个简单的例子:


#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
static int count_ = 0;
std::recursive_mutex rmtx;
void fun_1()
{
	rmtx.lock();
	rmtx.lock();
	rmtx.lock();
	std::cout << "fun_1 3 LOCKs" << std::endl;
	std::this_thread::sleep_for(std::chrono::milliseconds(3000));//让调用此函数的线程休眠某长时间。
	std::cout << "fun_1 2 LOCKs" << std::endl;
	rmtx.unlock();
	std::this_thread::sleep_for(std::chrono::milliseconds(3000));
	std::cout << "fun_1 1 LOCKs" << std::endl;
	rmtx.unlock();
	std::this_thread::sleep_for(std::chrono::milliseconds(3000));
	std::cout << "fun_1 0 LOCKs" << std::endl;
	rmtx.unlock();
}
void fun_2()
{
	while (!rmtx.try_lock()){}
	std::cout << "fun_2 LOCK" << std::endl;
	rmtx.unlock();
}
void fun_3()
{
	rmtx.lock();
	std::cout << "fun_3 LOCK" << std::endl;
	rmtx.unlock();
}
int main()
{
	std::thread t1(fun_1);
	std::thread t2(fun_2);
	t1.join();
	t2.join();
	system("pause");
	return 0;
}
tips:这里不管线程t2调用fun_2或者fun_3都是一样的,fun_2只是希望大家了解一下try_lock()方法。)

输出的结果如下:

注意打印前四行之间的间隔都是三秒,最后两行同时被打印,说明当线程1的3个锁都被解锁之后(每三秒解锁一次),其他线程瞬间可以对互斥量上锁,然后进行操作。

这篇文章先介绍前两种互斥量,后面有时间会把下面两个互斥量补充上,谢谢阅读。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值