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

线程同步

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

解决方案

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

条件变量接口介绍

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.ccg++ -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.ccg++ -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.mzph.cn/bicheng/20132.shtml

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

相关文章

系统架构设计师【第9章】: 软件可靠性基础知识 (核心总结)

文章目录 9.1 软件可靠性基本概念9.1.1 软件可靠性定义9.1.2 软件可靠性的定量描述9.1.3 可靠性目标9.1.4 可靠性测试的意义9.1.5 广义的可靠性测试与狭义的可靠性测试 9.2 软件可靠性建模9.2.1 影响软件可靠性的因素9.2.2 软件可靠性的建模方法9.2.3 软件的可靠性模…

实物资产的市场主线将逐步回归

民生证券认为&#xff0c;投资者逐渐意识到长期趋势并没有发生变化&#xff0c;这或许正是本周最大的变化。在预期博弈重回冷静期后&#xff0c;去金融化背景下实物资源占优的市场主线也将逐步回归。 1 高低切换后的冷静期 从4月下旬至上周&#xff0c;A股市场呈现出由高位资产…

用windows server backup备份文件夹到网络共享文件夹并恢复

一、备份 开始 运行windows server backup,在右边的窗格中点击“备份计划” 选择备份配置 因为我们要备份的是一个文件夹&#xff0c;所以&#xff0c;选“自定义”&#xff0c;卷即为磁盘分区。 选择要备份的项 点击添加项目&#xff0c;可依次添加多个备份项目。 勾选需要…

汽车MCU虚拟化--对中断虚拟化的思考(2)

目录 1.引入 2.TC4xx如何实现中断虚拟化 3.小结 1.引入 其实不管内核怎么变&#xff0c;针对中断虚拟化无非就是上面两种&#xff0c;要么透传给VM&#xff0c;要么由Hypervisor统一分发。汽车MCU虚拟化--对中断虚拟化的思考(1)-CSDN博客 那么&#xff0c;作为车规MCU龙头…

MySQL 视图(2)

上一篇&#xff1a;MySQL视图&#xff08;1&#xff09; 基于其他视图 案例对 WITH [CASCADED | LOCAL] CHECK OPTION 进行释义 创建视图时&#xff0c;可以基于表 / 多个表&#xff0c;也可以使用 其他视图表 / 其他视图 其他视图 的方式进行组合。 总结 更新视图&#x…

【HTML】tabindex

当给 div 标签以 button 角色&#xff1a; <div role"button">这时要指定其 tabindex&#xff0c;因此正确的写法是&#xff1a; <div role"button" tabindex"0">索引值不应当大于0&#xff0c;见a11y-positive-tabindex

Open3D(C++) Ransac拟合多项式曲线

目录 一、算法原理一、代码实现三、结果展示本文由CSDN点云侠原创,Open3D(C++) Ransac拟合多项式曲线,爬虫自重。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、算法原理 RANSAC(Random Sample Consensus)是一种用于拟合模型的迭…

设计模式深度解析:分布式与中心化

设计模式在软件开发中扮演着至关重要的角色,它们提供了一套经过验证的解决方案,用于解决常见的设计问题。在分布式和中心化这两种不同的系统架构中,设计模式的应用也有所不同。以下是对这两种架构下设计模式的深度解析: 分布式系统设计模式 在分布式系统中,由于系统被拆…

004 仿muduo实现高性能服务器组件_Buffer模块与Socket模块的实现

​&#x1f308;个人主页&#xff1a;Fan_558 &#x1f525; 系列专栏&#xff1a;仿muduo &#x1f339;关注我&#x1f4aa;&#x1f3fb;带你学更多知识 文章目录 前言Buffer模块Socket模块 小结 前言 这章将会向你介绍仿muduo高性能服务器组件的buffer模块与socket模块的实…

【Leetcode 706 】设计哈希映射——数组嵌套链表(限制哈希Key)

题目 不使用任何内建的哈希表库设计一个哈希映射&#xff08;HashMap&#xff09;。 实现 MyHashMap 类&#xff1a; MyHashMap() 用空映射初始化对象void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中&#xff0c;则更新其…

MATLAB的plot3使用技巧|更改视角|例程分享链接

plot3命令 MATLAB的plot3函数是用来绘制3D图形的函数。它可以将三维数据可视化为线段、点、曲线等形式。plot3函数可以用于绘制三维空间中的曲线、曲面、散点图等。 plot3函数的基本用法是&#xff1a; plot3(X,Y,Z)&#xff1a;绘制三维线段&#xff0c;其中X、Y、Z分别是包…

两个双指针 的 “他“和“ 她“会相遇么? —— “双指针“算法 (Java版)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能接…

MySQL入门学习-查询进阶.UNION

UNION操作符用于合并两个或多个SELECT语句的结果集。它可以将多个查询结果合并为一个结果集&#xff0c;这在需要从多个表中获取数据并将它们组合在一起时非常有用。下面是一个使用UNION的示例代码&#xff1a; SELECT column1, column2,...FROM table1UNIONSELECT column1, c…

springboot kafka 提高拉取数量

文章目录 背景问题复现解决问题原理分析fetch.min.bytesfetch.max.wait.ms源码分析ReplicaManager#fetchMessages 背景 开发过程中&#xff0c;使用kafka批量消费&#xff0c;发现拉取数量一直为1&#xff0c;如何提高批量拉取数量&#xff0c;记录下踩坑记录。 问题复现 ka…

攻防对抗少丢分,爱加密帮您筑起第二防线

应用程序通常处理和存储大量的敏感数据&#xff0c;如用户个人信息、财务信息、商业数据、国家数据等&#xff0c;用户量越大的应用程序&#xff0c;其需要存储和保护的用户数据越多。因此应用层长期是攻击方的核心目标&#xff0c;传统应用安全依靠防火墙(FireWall)、入侵检测…

1.7 协议层次和服务模型

协议层次 网络是一个复杂的系统!  网络功能繁杂&#xff1a;数字信号的物理信 号承载、点到点、路由、rdt、进程区分、应用等 现实来看&#xff0c;网络的许多构成元素和设备:  主机  路由器  各种媒体的链路  应用  协议  硬件, 软件 Q:如何组织和实现这个…

Linux上实现ssh免密通讯

Linux上实现ssh免密通讯 1.SSH互信原理2.SSH所需的RPM包3.两台机器实现互信4.常见问题及处理 1.SSH互信原理 SSH&#xff08;Secure Shell&#xff09;是一种安全的传输协议&#xff0c;它能让Linux系统中的服务器和客户端之间进行安全可靠的通讯。 SSH使用加密的传输方式&…

iOS组件化 方案 实现

iOS组件化 组件化的原因现在流行的组件化方案方案一、url-block &#xff08;基于 URL Router&#xff09;方案二、protocol调用方式解读 方案三、target-action调用方式解读 gitHub代码链接参考 组件化的原因 模块间解耦模块重用提高团队协作开发效率单元测试 当项目App处于…

网络原理-四

一、续 当窗口大小为0,意味着缓冲区满了,此时发送方,就因该暂停发送,发送方会周期性的除法 " 窗口探测包 " ,并不携带载荷,这样的包对于业务不产生影响,只是为了触发ACK,一旦查询出来的结果是非0,缓冲区右有空间了,发送方就可以继续发送. 二、拥塞控制 要限制发送方…

一步一步写线程之十三队列间的消息通知

一、线程和分布式的通信 随着技术的不断发展&#xff0c;多线程和分布式通信愈发的普及。那么在这种场景下的如何进行数据的通信&#xff0c;便成为了一个非常典型的问题。无论是多线程还是分布式&#xff0c;其实其抽象出来的通信机制都是类似的。或者说换句话&#xff0c;多…