前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 多线程互斥锁(mutex,lock,lock_guard)

C++ 多线程互斥锁(mutex,lock,lock_guard)

作者头像
Ch_Zaqdt
发布2020-02-14 17:24:35
20.1K0
发布2020-02-14 17:24:35
举报
文章被收录于专栏:Zaqdt_ACMZaqdt_ACMZaqdt_ACM

       对于互斥锁我们要先知道为什么要用互斥锁?它能解决什么问题?

       根据这两个问题,可以来举个例子说明一下,假如现在我们要求1-10000的和,然后我们为了提高效率,我们建立两个线程同时去计算[1,5000)的和以及[5000,10001)的和,那么用于计算和的变量都用相同的ans来获取结果,代码如下:

#include <iostream>
#include <thread>

void work1(int& sum) {
	for (int i = 1; i < 5000; i++) {
		sum += i;
	}
}

void work2(int& sum) {
	for (int i = 5000; i <= 10000; i++) {
		sum += i;
	}
}

int fun() {
	int sum = 0;
	for (int i = 1; i <= 10000; i++) {
		sum += i;
	}
	return sum;
}

int main()
{
	int ans = 0;
	std::thread t1(work1, std::ref(ans));
	std::thread t2(work2, std::ref(ans));
	t1.join();
	t2.join();
	std::cout << "sum1 : " << ans << std::endl;
	std::cout << "sum2 : " << fun() << std::endl;
	return 0;
}

       为了区别多线程的计算结果,我用fun函数求的结果与其作比较,然后运行的结果如下图所示:

       我们发现两次的运算结果并不相同,那么我们可以分析一下原因,因为在计算过程中的sum是一个引用,是他们的共享资源,所以当一个线程正在计算+i的时候,此时还没有运算结束,就被切到了另一个线程中,然后在这个线程中可能会计算了很多次+i的操作,然后再切回那个线程中时,计算结果可能就会覆盖掉另一个线程的计算结果,因此这样求出来的数一定是比正确结果要小的,所以为了避免这种情况的发生,引入了互斥锁。

       互斥锁的重点在于他是一个锁,简单来说就是我们用锁将两个线程中计算过程分别用mutex锁上,那么当一个线程正在计算的时候,另一个线程就会等待这个计算的完成。大致流程是这样的,当work1准备计算sum+=i的时候,用mutex将线程其锁上,如果此时sum+=i还没有计算完就切到了work2的线程时,就会通过mutex检测到已经被锁上了,那么work2就会在此等待work1的计算完成,当work1的计算完成以后就会把锁解开,然后进行下一步的计算。所以两个线程种的计算过程都是加锁-计算-解锁的过程,这样就不会出现上述所说的那种情况了。

       互斥锁的实现过程很简单,mutex是一个类,首先我们要先创建出类对象std::mutex mylock,然后在你需要锁的代码块前后加上mylock.lock()和mylock.unlock(),就可以实现互斥锁的加锁和解锁了。可以具体实现可以看下面的代码:

#include <iostream>
#include <thread>
#include <mutex>

void work1(int& sum, std::mutex& mylock) {
	for (int i = 1; i < 5000; i++) {
		mylock.lock();
		sum += i;
		mylock.unlock();
	}
}

void work2(int& sum, std::mutex& mylock) {
	for (int i = 5000; i <= 10000; i++) {
		mylock.lock();
		sum += i;
		mylock.unlock();
	}
}

int fun() {
	int sum = 0;
	for (int i = 1; i <= 10000; i++) {
		sum += i;
	}
	return sum;
}

int main()
{
	std::mutex mylock;
	int ans = 0;
	std::thread t1(work1, std::ref(ans), std::ref(mylock));
	std::thread t2(work2, std::ref(ans), std::ref(mylock));
	t1.join();
	t2.join();
	std::cout << "sum1 : " << ans << std::endl;
	std::cout << "sum2 : " << fun() << std::endl;
	return 0;
}

       这是第一种互斥锁的实现方法。还有一种是用lock_guard类模板,它的内部结构很简单,只有构造函数和析构函数,所以也很容里理解它的工作原理,在实例化对象时通过构造函数实现了lock,在析构函数中实现了unlock的操作。这样就可以避免忘记unlock的情况,具体的实现看下面的代码:

#include <iostream>
#include <thread>
#include <mutex>

void work1(int& sum, std::mutex& mylock) {
	for (int i = 1; i < 5000; i++) {
		std::lock_guard<std::mutex> mylock_guard(mylock);
		sum += i;
	}
}

void work2(int& sum, std::mutex& mylock) {
	for (int i = 5000; i <= 10000; i++) {
		std::lock_guard<std::mutex> mylock_guard(mylock);
		sum += i;
	}
}

int fun() {
	int sum = 0;
	for (int i = 1; i <= 10000; i++) {
		sum += i;
	}
	return sum;
}

int main()
{
	std::mutex mylock;
	int ans = 0;
	std::thread t1(work1, std::ref(ans), std::ref(mylock));
	std::thread t2(work2, std::ref(ans), std::ref(mylock));
	t1.join();
	t2.join();
	std::cout << "sum1 : " << ans << std::endl;
	std::cout << "sum2 : " << fun() << std::endl;
	return 0;
}

       这样就在每次循环一次后会自动的构建互斥锁对象,循环完了就会析构掉这个互斥锁。当然为了使用的更灵活方便,我们可以通过大括号来规定实现的范围。比如下面这样:

  {
 	std::lock_guard<std::mutex> mylockguard(mylock);
	/*...
	   中间用来写需要加锁的内容
	*/
  }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-02-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档