【C++11多线程】多线程之数据共享
@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
- 点赞
- 收藏
- 关注作者
评论(0)