【linux】线程同步和生产消费者模型

news2024/11/20 12:42:35

线程同步

当我们多线程访问同一个临界资源时,会造成并发访问一个临界资源,使得临界资源数据不安全,我们引入了锁的概念,解决了临界资源访问不安全的情况,对于线程而言竞争锁的能力有强有弱,对于之前就抢到锁的线程,当他释放锁后,由于不用做什么准备工作,他竞争锁的能力很强,导致这个线程反复的争夺锁,来访问临界资源,导致其他线程处于饥饿状态
同步:同步问题是保证数据安全的情况下,让我们的线程具有一定的顺序性

解决方案

条件变量的引入
当多线程来访问临界资源时,首先不会让他去访问临界资源,而是将这个线程放入条件变量维护的队列中去,等待临界资源就绪,举个例子,一个幼儿园里面,到了饭点,而小朋友们是每一个线程,而饭就是临界资源,每次只能有一个孩子在餐厅里面打饭,这就是锁,每个孩子跑步速度不一样,竞争锁的能力不一样,为了避免一个孩子一直在餐厅不走,一直吃饭,幼儿园老师做了这个规定,每个小朋友吃饭必须去排队,刚吃完的孩子还想吃的话,必须排队在队的后面。而条件变量维护的队列类比与排队,每个线程访问完临界资源之后必须在条件变量维护的队列后面排队
在这里插入图片描述
在这里插入图片描述

条件变量接口介绍

1、主要应用函数:

pthread_cond_init()函数
功能:初始化一个条件变量
pthread_cond_wait()函数
功能:阻塞等待一个条件变量
pthread_cond_signal()函数
功能:唤醒至少一个阻塞在条件变量上的线程
pthread_cond_broadcast()函数
功能:唤醒全部阻塞在条件变量上的线程
pthread_cond_destroy()函数
功能:销毁一个条件变量

函数分析

1.初始化一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 

参2:attr表条件变量属性,通常为默认值,传NULL即可

也可以使用静态初始化的方法,初始化条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2.阻塞等待一个条件变量

 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 

函数作用:

阻塞等待条件变量cond(参1)满足
释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);

3.唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_signal(pthread_cond_t *cond);

4.唤醒全部阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond);

5.销毁一个条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

代码实现线程同步

1.makefile编写

mycond:mycond.cc
	g++ -o mycond mycond.cc -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f mycond

2.mycond.cc

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;

int cnt=0;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

void*fun(void*args)
{
  pthread_detach(pthread_self());
  uint64_t num=(uint64_t)args;
  cout<<"pthread:"<<num<<"create success"<<endl;
  while(1)
  { pthread_mutex_lock(&mutex);
   cout<<"pthread:"<<num<<",cnt:"<<cnt++<<endl;
    pthread_mutex_unlock(&mutex);
  }

}
int main()
{

for( uint64_t i=0;i<5;i++)
{
   pthread_t tid;
   pthread_create(&tid,nullptr,fun,(void*)i);
}
while(1)
{


    
}
}

代码解释:
初始化一个锁,一个条件变量,临界资源cnt,每个线程在自己要执行的fun函数内,需要访问临界资源cnt,对cnt++,在主函数中创建5个线程,为了防止主线程退出,所有线程都退出,所以让主线程死循环,在每个fun函数里面实现线程分离,不需要主线程来等待回收其他线程,让操作系统自己回收

在这里插入图片描述
由于线程2竞争锁的能力强,每次都是线程2来访问临界资源。
为了解决一个线程竞争锁的能力强,使用线程同步,先实现代码在来解释

#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;

int cnt=0;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

void*fun(void*args)
{
  pthread_detach(pthread_self());
  uint64_t num=(uint64_t)args;
  cout<<"pthread:"<<num<<"create success"<<endl;
  while(1)
  { pthread_mutex_lock(&mutex);
   pthread_cond_wait(&cond,&mutex);//新增行
   cout<<"pthread:"<<num<<",cnt:"<<cnt++<<endl;
    pthread_mutex_unlock(&mutex);
    
  }

}
int main()
{

for( uint64_t i=0;i<5;i++)
{
   pthread_t tid;
   pthread_create(&tid,nullptr,fun,(void*)i);
}
while(1)
{
sleep(1);//新增行
pthread_cond_signal(&cond);//新增行
cout<<"signal one thread..."<<endl;    //新增行
}
}

pthread_cond_wait函数可以将刚申请锁的线程让其加入队列,让其休眠,该函数还会让对应的线程释放锁,这样一轮下来,所有想访问临界资源的线程都会出现在条件变量维护的队列中,等待唤醒,一次唤醒一个,让他们去访问临界资源,在主线程中来唤醒在维护条件变量对应的队列中其他线程,去访问临界资源,sleep作用防止打印太快看不到效果。
在这里插入图片描述
五个线程按照顺序去访问临界资源


cp问题(生产消费者模型)

在这里插入图片描述
此时有三种关系:
生产者和生产者:互斥
消费者和生产者:互斥,同步(一个放,一个拿,肯定要保证顺序问题)
消费者和消费者:互斥
3种关系:生产者和生产者,消费者和消费者,生产者和消费者
2种角色:生产者和消费者
1个交易场所:特定结构的内存空间

特点:支持忙闲不均(对比冯诺依曼结构)
生产和消费进行解耦

代码实现生产消费者模型

makefile实现

cp:cp.cc
  g++ -o cp cp.cc -std=c++11 -lpthread
.PHONY:clean
clean:
  rm -f cp

cp.cc(未完成)


#include<iostream>
#include<pthread.h>
#include"blockqueue.hpp"
#include<unistd.h>

using namespace std;
void*consumer(void*args)
{
Blockqueue<int>*cq=static_cast<Blockqueue<int>*>(args);
while(1)
{
//消费数据,将队列中的数据pop
int data=cq->pop();
cout<<"消费了一个数据:"<<data<<endl;
}
}
void* productor(void*args)
{
 int data=0;
Blockqueue<int>*pq=static_cast<Blockqueue<int>*>(args);
while(1)
{ sleep(1);

 //生产数据放到队列中去
  pq->push(data);
  cout<<"生产了一个数据:"<< data++ <<endl;
}

}




int main()
{
pthread_t p,c;
Blockqueue<int>*st=new Blockqueue<int>();


 pthread_create(&p,nullptr,productor,st);
 pthread_create(&c,nullptr,consumer,st);


pthread_join(p,nullptr);
pthread_join(c,nullptr);//线程等待回收
delete st;
return 0;

}

在这里插入图片描述
主线程创建两个线程,一个是生产者,一个是消费者,生产者将数据插入在阻塞队列中,消费者将数据取出阻塞队列中的数据,阻塞队列相当于临界资源,主线程中new一个阻塞队列对象作为共享的资源,在各个线程要执行的函数里面,pthread_create最后一个参数可以是资源的起始地址,我们可以传阻塞队列类对象过去,让生产者线程和消费者线程都可以访问到
blockqueue.hpp

#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;

template<class T>
class Blockqueue
{

static const int defalutnum=5;
public:
Blockqueue(int maxcap=defalutnum)
:maxcap_(maxcap)
 {
  pthread_mutex_init(&mutex_,nullptr);
  pthread_cond_init(&p_cond_,nullptr);
    pthread_cond_init(&c_cond_,nullptr);
 }
T pop()
{
  T out=q_.front();
  q_.pop();
  return out;
}
void push(const T&in)
{
q_.push(in);
}
~Blockqueue()
 {
  pthread_mutex_destroy(&mutex_);
  pthread_cond_destroy(& p_cond_);
    pthread_cond_destroy(& c_cond_);
    
 }
private:
queue<T>q_;
int maxcap_;
pthread_mutex_t mutex_;
pthread_cond_t p_cond_;
pthread_cond_t c_cond_;

};

由于该阻塞队列是两者共享的,临界资源,防止并发访问,要实现安全保护,所以要使用锁,因为生产者和消费者都会对临界资源访问,两者对锁的竞争能力不一样,可能会导致饥饿问题,使用线程同步,所以要用条件变量
maxcap为队列最大可以放几个值,这是我们规定的,为了解决忙而不均,同步问题,达到最大,就让生产者线程休眠,如果阻塞队列的个数为0,就让消费者线程休眠,所以这里需要两个条件变量分别维护。


现在要解决的是临界资源的保护,保证多线程并发安全,使用锁,还要就是两个线程竞争锁的能力不同,可能导致另一个线程饥饿问题,使用线程同步
在这里插入图片描述

blockqueue.hpp

#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>
using namespace std;

template<class T>
class Blockqueue
{

static const int defalutnum=5;
public:
Blockqueue(int maxcap=defalutnum)
:maxcap_(maxcap)
 {
  pthread_mutex_init(&mutex_,nullptr);
  pthread_cond_init(&p_cond_,nullptr);
   pthread_cond_init(&c_cond_,nullptr);
   max_water=(maxcap*2)/3;
   min_water=maxcap/3;
 }
T pop()
{ pthread_mutex_lock(&mutex_);
 while(q_.size()==0)
    {
    pthread_cond_wait(&c_cond_,&mutex_);
    }
  T out=q_.front();
  q_.pop();
  if(q_.size()<min_water)
  pthread_cond_signal(&p_cond_);
  pthread_mutex_unlock(&mutex_);
  return out;
}
void push(const T&in)
{
pthread_mutex_lock(&mutex_);
while(q_.size()==maxcap_)
{pthread_cond_wait(&p_cond_,&mutex_);}
q_.push(in);
if(q_.size()>max_water)
pthread_cond_signal(&c_cond_);
pthread_mutex_unlock(&mutex_);
}
~Blockqueue()
 {
  pthread_mutex_destroy(&mutex_);
  pthread_cond_destroy(&p_cond_);
   pthread_cond_destroy(&c_cond_);

    
 }
private:
queue<T>q_;
int maxcap_;
pthread_mutex_t mutex_;
pthread_cond_t p_cond_;
pthread_cond_t c_cond_;
int max_water;
int min_water;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1720304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在CentOS7下构建TeamSpeak服务器并增加网易云点歌插件

文章目录 部署TeamSpeak创建一个新用户下载并解压服务端下载解压 启动服务端同意许可协议启动与配置开放端口设置开机自启 客户端连接 部署TS3AudioBot并添加网易云插件安装ffmpeg下载TS3AudioBot本体与插件并解压配置TS3AudioBot启动设置开机自启 部署网易云API安装git安装Nod…

【模型架构】学习RNN、LSTM、TextCNN和Transformer以及PyTorch代码实现

一、前言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;模型架构的不断发展极大地推动了技术的进步。从早期的循环神经网络&#xff08;RNN&#xff09;到长短期记忆网络&#xff08;LSTM&#xff09;、Transformer再到当下火热的Mamba&#xff08;放在下一节&a…

Linux|虚拟机|Windows 11 家庭版的Hyper虚拟机服务开启

前言&#xff1a; Windows11的版本是比较多的&#xff0c;但有的时候笔记本预装的可能是家庭版&#xff0c;而家庭版的Windows通常是不支持虚拟机的&#xff0c;也就是说Hyper服务根本就看不到 Windows的程序和功能大体如下&#xff1a; &#x1f197;&#xff0c;那么如何开…

ChaosBlade混沌测试实践

ChaosBlade: 一个简单易用且功能强大的混沌实验实施工具 官方仓库&#xff1a;https://github.com/chaosblade-io/chaosblade 1. 项目介绍 ChaosBlade 是阿里巴巴开源的一款遵循混沌工程原理和混沌实验模型的实验注入工具&#xff0c;帮助企业提升分布式系统的容错能力&…

Nuxt3项目实现 OG:Image

目录 前言 1、安装 2、设置网站 URL 3、启用 Nuxt DevTools 4、创建您的第一个Og:Image a. 定义OG镜像 b. 查看您的Og:Image 5、自定义NuxtSeo模板 a. 定义 NuxtSeo模板 b. 使用其他可用的社区模板 6、创建自己的模板 a. 定义组件 BlogPost.vue b. 使用新模板 c.…

【爱空间_登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

模型 FABE(特性 优势 好处 证据)法则

说明&#xff1a;系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。特性、优势、好处、证据&#xff0c;一气呵成。 1 FABE法则的应用 1.1 FABE法则营销商用跑步机 一家高端健身器材公司的销售代表正在向一家新开的健身房推销他们的商用跑步机。以下…

孩子用的灯什么样的好?安利几款适合孩子用的护眼台灯

随着学生们重返校园&#xff0c;家长和孩子们忙于新学期的准备工作&#xff0c;眼睛健康的考量自然也在其中。这也是为何近年来护眼台灯越来越受到欢迎的原因之一。作为一个长期近视并且日常用眼时间较长的人&#xff0c;我本人对护眼台灯有着长期的使用经历&#xff0c;并对它…

如何创建一个Angular项目(超简单)

1、安装Node.js&#xff08;官网Node.js下载&#xff09; 2、运行node -v和npm -v两条命令&#xff08;检验是否下载成功Node.js&#xff09; 3、npm i -g cnpm --registryhttps://registry.npmmirror.com&#xff08;用npm安装cnpm&#xff0c;将镜像源设置为国内镜像源&…

接入knife4j-openapi3访问/doc.html页面空白问题

大概率拦截器拦截下来了&#xff0c;我们F12看网络请求进行排查 都是 /webjars/ 路径下的资源被拦截了&#xff0c;只需在拦截器中添加该白名单即可"/webjars/**" 具体配置如下&#xff1a; Configuration public class WebConfig implements WebMvcConfigurer {priv…

云端数据提取:安全、高效地利用无限资源

在当今的大数据时代&#xff0c;企业和组织越来越依赖于云平台存储和处理海量数据。然而&#xff0c;随着数据的指数级增长&#xff0c;数据的安全性和高效的数据处理成为了企业最为关心的议题之一。本文将探讨云端数据安全的重要性&#xff0c;并提出一套既高效又安全的数据提…

图像加雾算法的研究与应用

目录 前言 一、图像加雾 1、基于传统方法的雾图合成 2、基于深度学习的雾图合成 3、基于Lightroom Classic实现软件加雾 4、基于深度图的方法实现加雾 二、开源的数据集 三、参考文章 前言 在去雾任务当中&#xff0c;训练和评估去雾算法需要大量的带有雾霾和无雾霾的…

超维小课堂 | 1、Pixhawk硬件平台和PX4固件以及QGC地面站之间的关联和区别

Pixhawk硬件平台和PX4固件以及QGC地面站之间的关联和区别 一、Pixhawk是无人机飞控的硬件平台。 主要集成了相关的单片机芯片和各种传感器的外围电路&#xff0c;因此&#xff0c;可以直观的认为Pixhawk一个由各种硬件模块整合成的硬件平台&#xff0c;类似于集成好的单片机开…

【Qt知识】Qt中的对象树是什么?

在深入Qt编程的世界时&#xff0c;你会频繁遇到一个核心概念——对象树&#xff08;Object Tree&#xff09;。这个概念是Qt框架管理内存、处理事件和组织用户界面元素的基础。 什么是Qt对象树&#xff1f; 如果你的Qt应用程序就像一片茂盛的森林&#xff0c;而这片森林中的每…

CameraProvider启动流程

从Android 8.0之后&#xff0c;Android 引入Treble机制&#xff0c;主要是为了解决目前Android 版本之间升级麻烦的问题&#xff0c;将OEM适配的部分vendor与google 对android 大框架升级的部分system部分做了分离&#xff0c;一旦适配了一个版本的vendor信息之后&#xff0c;之…

炫云亮相第二十届中国国际动漫节国际动漫游戏商务大会!

5月28日-29日&#xff0c;备受瞩目的第二十届中国国际动漫节国际动漫游戏商务大会(iABC2024)在杭州滨江开元名都大酒店隆重召开&#xff01;本届大会以动漫IP为核心&#xff0c;从源头到全系列数字内容&#xff0c;探索创新协同、融合发展、价值转化&#xff0c;并对重点作品和…

校园安保巡逻机器人

2023年8月5日&#xff0c;陕西西安一高校实验室起火冒烟&#xff0c;导致学校化学实验室发生火灾。2022年8月3日&#xff0c;一名歹徒持械闯入江西吉安安福县城的一家私立幼儿园&#xff0c;对着无辜的幼儿行凶伤人&#xff0c;造成3死6伤。 像这样的事故有不断地发生&#xf…

计算机基础学习路线

计算机基础学习路线 整理自学计算机基础的过程&#xff0c;虽学习内容众多&#xff0c;然始终相信世上无难事&#xff0c;只怕有心人&#xff0c;期间也遇到许多志同道合的同学&#xff0c;现在也分享自己的学习过程来帮助有需要的。 一、数据结构与算法 视频方面我看的是青…

DNF手游攻略:勇士进阶指南!

在即将到来的6月5日&#xff0c;《DNF手游》将迎来一场盛大的更新&#xff0c;此次更新带来了大量新内容和玩法&#xff0c;极大丰富了游戏的体验。本文将为广大玩家详细解析此次更新的亮点&#xff0c;包括新增的组队挑战玩法“罗特斯入门团本”、新星使宠物的推出、宠物进化功…

【Android】

hint在text显示提示内容 设置主键&#xff0c;在mainactivity // 获取SharedPreferences对象存放的用户名和密码&#xff0c;并设为相应组件的值 //指定key的值&#xff0c;及获取不到值时使用的默认值 String sName sp.getString("name", "unknown")…