【C++11多线程】多线程之数据共享

举报
人才程序员 发表于 2023/10/29 20:38:22 2023/10/29
【摘要】 @TOC 前言本节课的主要内容是解决线程中数据共享的问题提示:以下是本篇文章正文内容,下面案例可供参考 一、数据共享的定义以及示例问题数据共享的定义:在多个线程中读/写一个变量。那我们首先要知道:变量能同时读取一个数据但不能同时写和读或者一起写同一个数据。数据我们可以定义为全局变量或类中的一个成员。 二、解决方案_互斥量、lock()、unlock() 引导功能为:myin()加数据到lis...

@TOC


前言

本节课的主要内容是解决线程中数据共享的问题


提示:以下是本篇文章正文内容,下面案例可供参考

一、数据共享的定义以及示例问题

数据共享的定义:在多个线程中读/写一个变量。
那我们首先要知道:变量能同时读取一个数据但不能同时写和读或者一起写同一个数据。
数据我们可以定义为全局变量或类中的一个成员。

二、解决方案_互斥量、lock()、unlock()

引导

功能为:myin()加数据到list中,myou()输出myin加入的数据
myin()功能为写,myou为读
大家可以试一下自己实现:

1、mutex互斥量

互斥量是上什么:如果不需要信号量的计数能力,有时可以使用信号量的一个简化版本,称为互斥量(mutex)
简单来说就是用来锁住一段代码,其他线程再次锁时就需要等待线程解锁

他在C++中头文件为:#include <mutex>
他是一个类
他的定义为:mutex m;

2、mutex::lock()锁

作用:用来锁住一段代码,其他线程使用时就需要等待解锁。
可以有效的缓解同时读/又读又写的线程

mutex m;
m.lock();//如果没有及时解锁,则:锁到程序结束,并且其他人拿不到锁
........
cout<<"hello world"<<endl;

3、mutex::unlock()解锁

作用:用来解锁一段代码,其他线程可以在次锁这个互斥量。

锁的意义:不让变量同时读写

4、范例演示

讲解:我们有2个线程,他们又读又写,所以我们需要锁住变量以保证数据安全。
我们使用完数据后也需要解锁,要不然其他线程会卡到lock()永远执行不到

class A
{
public:
	//功能为:myin()加数据到li中,myou()输出myin加入的数据
	void myin()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "插入数据:" << i << endl;
			m.lock();//加锁保护数据安全
			li.push_back(i);
			m.unlock();//使用完后需要及时解锁
		}
	}
	void myou()
	{
		for (int i = 0; i < 100000; i++)
		{
			m.lock();//加锁保护数据安全
			if (!li.empty())//不为空时输出数据并删除他
			{
				//输出数据
				cout << "数据输出:" << li.front() << endl;
				li.pop_front();
			}
			else//为空则取不到
				cout << "队列为空" << endl;
			m.unlock();//使用完后需要及时解锁
		}
	}
	//定义一个list队列
	list<int> li;
	//定义一个互斥量
	mutex m;
};
int main()
{
	A a;
	//创建2个线程
	thread t(&A::myin);
	thread th(&A::myou);
	//让主线程等待
	t.join();
	th.join();
	return 0;
}

现在就可以稳定地跑起来了。

三、lock_gurad类模板、死锁及解决方案

1、为什么要使用lock_gurad

我相信,有些人lock()后总会忘记unlock()导致其他线程执行不到,卡在lock()那
怎么办呢?系统/C++给我们提供了一个类模板,使用他就不用unlock()了

2、lock_gurad的使用

他其实就是一个类模板
他的作用是不用unlock锁了,他自动unlock()
改一下刚刚的范例:

void myin()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "插入数据:" << i << endl;
			lock_guard<mutex> lo(m);
			li.push_back(i);
		}
	}
	void myou()
	{
		for (int i = 0; i < 100000; i++)
		{
			lock_guard<mutex> lo(m);
			if (!li.empty())//不为空时输出数据并删除他
			{
				//输出数据
				cout << "数据输出:" << li.front() << endl;
				li.pop_front();
			}
			else
				cout << "队列为空" << endl;
		}
	}

他还是能正常运行,所以我们写的没有问题

他还有一个功能:变成unlock()
在lock_gurad构造函数参数2写adopt_lock

拓展知识std::lock:一次锁住多个mutex互斥量:
实际:类模板

mutex _1;
mutex _2;
std::lock<mutex>(_1,_2);

3、死锁情况的演示

大概情况讲解:线程A拿到mu1锁,在拿mu2锁,然后解锁mu2,解锁mu1 线程B呢:拿mu2锁,再拿mu1,然后解锁mu1,解锁mu2。这就导致有可能A,B都不解锁。卡在那:
解决方案:锁和解锁两个线程顺序要一样

四、unique_lock lock_gurad加强版

1、与lock_gurad的对比

为什么说unique_lock是 lock_gurad的加强版呢?
答:unique_lock用法更灵活,更多的参数,更高的效率

2、使用

unique_lock也是一个类模板
基本功能和lock_gurad一样一样的,那他灵活在哪呢?
成员函数,和构造函数的参数1多种多样:
构造函数参数1:
1).try_to_lock
没有拿到锁立马返回,立马unlock
怎么看自己有没有拿到锁呢
使用owns_lock函数

unique_lock<mutex> uni(m);
if(uni.owns_lock)
{
	cout<<"拿到了锁"<<endl;
	//干其他的事情
}
else
	cout<<"没有拿到锁"

2).defer_lock
初始化一个mutex
前提:此mutex没有加锁

成员函数:
1).lock()
手动lock()

2).unlock()
手动unlock()
unique_lock可以自己加解锁

3).release()放弃mutex的所以权,并返回mutex的指针
所有权指的是:放弃他的操作,简单的说:调用了release函数就不能操作那个绑定的mutex

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。