一个比较经典的生产者消费者模型(Linux C++实现)

Linux-生产者消费者模型 生产者消费者模型一级目录二级目录三级目录一级目录 一级目录 二级目录 三级目录 一级目录 阅读详情

 学习了几天Linux多线程,大多是看的别人的博客,并试着写了个小例子,现在也把学到的一些东西记录下来,供以后查阅,属于初级入门的范围,望共勉。

  进程是系统中程序执行和分配资源的基本单位,每个进程都有自己独立的数据段,代码段和堆栈段。而线程是系统中独立运行的最小单位,可以说操作系统分配CPU时间的最小单位,可以叫轻型的进程。一个进程可以有多个线程,在多进程情况下,每个进程都有着自己的地址空间,消耗资源比较大,而多线程共享进程的地址空间,消耗资源很小。并且在系统调度方面,线程共享地址空间,因此切换速度远远大于进程间的切换速度。并且线程间通信可以直接将共享数据提供给其他线程,通信更加方便省时。

  Linux多线程编程时需要包含头文件pthread.h,编译链接的时候需要 libpthread.a,编译的时候要加上-lpthread,以调用静态链接库,因为pthread并非linux系统默认的库。

  Linux多线程可以分为两个个部分:一个是线程的控制,另外一个是线程的通信。

线程控制主要是创建线程,线程终止等。

1. 线程的控制

主要有两个方面,一个是线程的创建,另外是线程的退出。

 

函数

功能

pthread_create

创建线程

pthread_self

获取线程本身ID

pthread_exit

线程终止

1.1线程创建:

           函数原型为

Int pthread_create(thread_t *thread,const pthread_attr_t *attr,void *(start_routine)(void*),void *arg);

返回值:若成功返回零,不成功返回错误编号。

返回成功时,由第一个参数thread指向的内存单元被设置为新创建线程的线程ID(此线程ID可以在线程运行过程中用pthread_self获得)。attr参数用于制定各种不同的线程属性。新创建的线程从start_routine函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。

  参数:

    thread:第一个参数为指向线程标识符的指针(可以在线程运行过程中用pthread_self获得)。

    attr:第二个参数用来设置线程属性。

    start_routine:第三个参数是线程运行函数的起始地址。

    arg:最后一个参数是运行函数的参数。

1.2线程退出:

  线程退出分为三种情况,一是线程运行结束自动退出,而是线程自己调用pthread_exit主动退出,三是被其他函数调用pthread_cancel终止。

  注意进程退出函数是exit,所以线程就用pthread_exit代替exit。

 

2.线程通信

2.1互斥锁

  线程互斥意味着两个线程不能同时进入代码段,Linux可以通过互斥体pthread_mutex_t定义互斥机制完成互斥操作,互斥体机制是在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望锁定一个已经上锁的互斥体,则该线程挂起,进入阻塞状态,直到上锁的线程释放互斥锁为止。

  互斥锁的操作主要包括以下几个步骤:

    互斥锁初始化:pthread_mutex_init

    互斥锁锁定:pthread_mutex_lock

    互斥锁尝试锁定:pthread_mutex_trylock

    互斥锁解锁:pthread_mutex_unlock

    互斥锁销毁:pthread_mutex_destroy

  互斥锁主要分为三种:

  分为快速互斥锁,递归互斥锁,检索互斥锁:

  这三种锁的主要区别在于其他为占有互斥锁的线程在希望得到互斥锁的时候是否需要阻塞等待:

  快速互斥锁是指调用线程会阻塞直到拥有互斥锁的线程释放为止;

  递归互斥锁能够成功返回并且增加调用线程在互斥上加锁的次数;

  检索互斥锁则为快速互斥锁的阻塞版本,他会立即返回并得到一个错误。

  Linux系统在缺省参数情况下创建的是快速互斥锁,而一般情况下windows系统是默认采用递归互斥锁的,所以多数有经验的linux开发人员都采用递归互斥锁,以保证和windows的一致性。当然,我们需要根据程序不同的情况定义所需的互斥锁。

  函数原型为int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *mutexattr);

    Mutexattr:PTHREAD_MUTEX_INITIALIZER快速互斥锁

    Mutexattr:PTHREAD_RECRUSIVE_MUTEX_INITIALIZER_NP递归互斥锁

    Mutexattr:PTHREAD_REEORCHECK_MUTEX_INITIALIZER_NP检索互斥锁

2.2条件变量condition vararble

  与互斥锁不同,条件变量是用来阻塞一个线程,直到某种特定条件发生为止。通常条件变量需要配合互斥锁同时使用。

  条件变量使线程可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量通信的一种机制,一个条件变量主要有两种状态:unsignaled(不成立状态)和signaled(成立状态)。而线程间通信主要有两个动作:一个线程等待(条件变量的条件成立),不成立则挂起,另外一个线程使条件成立(给出条件成立的信号)。

  条件的检测是在互斥锁保护下进行的,如果一个条件为假(unsignaled),一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另外一个线程改变条件使条件成立,它就会发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。

  主要操作函数如下:

    pthread_cond_init条件变量初始化。

    pthread_cond_wait阻塞等待:条件不成立,则线程将一直处于阻塞状态。

    pthread_cond_timedwait超时等待,一定时间后程序自动解除阻塞状态。

    pthread_cond_signal在使用这个函数的时候需要注意一个问题,就是linux条件变量自动复位的问题。此函数只将一个处于阻塞的线程解除阻塞状态,即使有多个线程等待此条件发生。

    pthread_cond_broadcast将所有等待条件发生的线程解除阻塞状态。

    pthread_cond_destroy销毁条件变量。

 

 

  说了这么多,还是通过一个小程序来增加理解:下面是一个生产者和消费者的例子,生产者不断往一个缓冲区中放数据,消费者不断从缓冲区中取数据。定义了两个条件变量,一个notempty(缓冲区不空),一个是notfull(缓冲区不满)

 

#include <stdio.h>
#include <pthread.h>
#define SIZE 4
struct product
{
    pthread_mutex_t mutux;
    pthread_cond_t notfull;
    pthread_cond_t notempty;
    int pos;
    int buf[SIZE];

};
struct product pdt;
void init(struct product * t)
{
    pthread_mutex_init(&t->mutux,NULL);
    pthread_cond_init(&t->notfull,NULL);
    pthread_cond_init(&t->notempty,NULL);
    t->pos=-1;

}
void put(struct product * t,int data)
{
    pthread_mutex_lock(&t->mutux);
    if(t->pos+1>SIZE-1)//the  array is full
    {
       pthread_cond_wait(&t->notfull,&t->mutux);
    }
    t->pos=t->pos+1;
    t->buf[t->pos]=data;
    printf("thread %d put a data %d  to  pos %d\n",pthread_self(),data,t->pos);
    pthread_cond_signal(&t->notempty);
    pthread_mutex_unlock(&t->mutux);
}


void get(struct product * t)
{
    pthread_mutex_lock(&t->mutux);
    if(t->pos<0)//the  array is full
    {
       pthread_cond_wait(&t->notempty,&t->mutux);
    }
    printf("thread %d get a data %d  to  pos %d\n",pthread_self(),t->buf[t->pos],t->pos);
    t->pos=t->pos-1;
    pthread_cond_signal(&t->notfull);
    pthread_mutex_unlock(&t->mutux);
}
void * putter(void *)
{
    int n;
    for(n=0;n<8;n++)
    {
       put(&pdt,n);
    }
}

void * getter(void *)
{
    int n;
    for(n=0;n<8;n++)
    {
       get(&pdt);
    }
}

int main(void )
{
   int err;
   pthread_t pt1,pt2,gt1,gt2;
   void * retval;
   init(&pdt);
   err=pthread_create(&pt1,NULL,putter,0);
   err=pthread_create(&pt2,NULL,putter,0);
   err=pthread_create(>1,NULL,getter,0);
   err=pthread_create(>2,NULL,getter,0);
   //主线程等待这四个线程结束

        pthread_join(pt1,&retval);

        pthread_join(pt2,&retval);

        pthread_join(gt1,&retval);

        pthread_join(gt2,&retval);

        return 0;

}

乍一看,程序没什么问题,其实,程序存在一个问题 ,就是当pos会越界。

当然,如果换成只有一个put线程和一个get线程程序就不存在问题了。

发现了当然两个put线程,两个get线程为什么会越界,仔细思考一下,不难得出结论,理解就更加深刻了~

似乎信号量机制可以很好的解决这个问题,待我学习完信号量后,用信号量改写此程序~~

RK3588 部署 YOLO26-Pose 人体姿态检测:Windows Docker 环境全流程 YOLO26-Pose 在 RK3588 上的部署流程是 PC 端 ONNX 导出 → RKNN 转换(INT8 量化)→ 板端推理。INT8 量化后 YOLO26s-Pose 约 35ms,YOLO26n-Pose 约 15ms,可以覆盖实时姿态检测的需求。后处理是 Python 实现的常见瓶颈——如果帧率不达标,先用 npy 保存模型输出在 PC 上验证解析逻辑的正确性,确认无误后再迁移到 C++ 实现。 阅读详情

相关推荐

Linux系统监控实战:夜莺监控+categraf采集器配置全流程(附常见问题排查)

本文详细介绍了在Linux系统中使用夜莺监控和categraf采集器进行系统监控的全流程配置,包括环境准备、核心配置文件(如config.toml和procstat.toml)详解、服务集成与验证、高级监控配置实战以及常见问题排查。通过实战案例和优化建议,帮助运维人员快速构建高效的监控体系。

xray4的博客 734

C++实战-Linux多线程(入门到精通)

1.进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。2.调用fork()来创建进程的代价相对较高(复制一份地址空间),即便采用"写时复制"机制,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性,这意味着fork()调用在时间上的开销依然不菲3.线程之间能够方便、快速地共享信息,只需要将数据复制到共享(栈不行,见上图)变量中即可4.创建线程比创建进程通常要快10倍甚至更多。

weixin_46120107的博客 4876

形式化方法(Formal Methods)和人工智能(Artificial Intelligence)

一、形式化方法(Formal Methods) ​ 形式化方法最初应用在硬件上,验证硬件的正确性,后面逐渐应用到软件编程中,能够在软件中找到软件的错误,验证软件的安全可靠。形式化方法更加接近离散数学,所以其应用难度非常大,同时学习成本也较高。再加上形式化方法的大多数理论还停留在上个世纪,由于程序越来越复杂,导致形式化方法进行验证需要大量人工和时间,以至于现在绝大部分软件开发人员完全没有考虑过这个方法,甚至没有听说过这个名词。 ​ 形式化方法最新的应用成果是在工业上,比如一些极其重要且对安全性要求极高的领域(

qq_37030400的博客 4004

【Linux系统】Linux多线程详解

由上图可以很好地理解线程为什么是CPU调度的基本单位,在CPU看来,它只关心一个独立的执行流,无论进程内部是一个还是多个执行流,CPU都是以task_struct为单位来调度的。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。在操作系统的的视角下,Linux下没有真正意义的线程,而是用进程模拟的线程(LWP,轻量级进程),所以Linux不会提供直接创建线程的系统调用,最多提供创建轻量级进程的接口。个字节,也就是1MB。

fufufu_的博客 6771

算法竞赛推荐书单

前言 入坑算法竞赛已经一年半了,在这期间也读了不少关于算法竞赛的书,这些书对我的帮助还是蛮大的。平时看题解多是个人零散的题解,很难以系统的去学习,认认真真的挑选1-2本书来阅读,无论是从思维能力还是文字表达能力都会得到提升,以下都是我自己读过的书进行一下分享,难度由浅入深,部分书籍浅尝而止。 入门:《啊哈!算法》 适合:总题量150以下初步掌握C语言的萌新 一本充满智慧和趣味的算法入门书。没有枯燥的描述,没有难懂的公式,一切以实际应用为出发点,通过幽默的语言配以可爱的插图来讲解算法。你更像是在阅读一个个轻松

TGX的博客 8239

Python逆向爬取Tik Tok,MsToken,X-Bogus以及signature

tiktok作为字节海外的自媒体平台,具有很非常大的价值,本文旨在教会大家实现Tik Tok的签名获取以及数据的最终爬取。

weixin_74305707的博客 3268

Linux多线程(二)C++多线程编程基础

进程:是操作系统分配和调度系统内存资源、CPU时间片等资源的基本单位,为正在运行的应用程序提供运行环境;线程:是操作系统/CPU能够进行运算调度的最小单位,它被包含在进程之中,进程包含一个或者多个线程。多线程:是实现并发/并行的手段,即多个线程同时执行,一般来说,进程可以理解为一件事情的完整解决方案,多线程就是把执行一件事情的完整步骤拆分为多个子步骤,然后这个多个子步骤同时进行。C++多线程:使用多个函数实现各自功能,不同函数生成不同功能,并同时执行。

weixin_46267139的博客 841

XMind文件解析黑科技:用Python实现版本自适应读取(兼容2010-2024)

本文深入探讨了如何利用Python实现XMind文件的版本自适应解析,兼容从2010到2024年的各版本。通过分析XMind文件作为ZIP压缩包的本质,文章提供了智能探测文件内部结构(JSON或XML)并构建健壮读取逻辑的完整方案,有效解决了版本兼容性难题,并演示了将解析数据转换为PlantUML等下游应用的实战案例。

weixin_29041767的博客 682

linux C++ 多线程浅尝

学习了几天Linux多线程,大多是看的别人的博客,并试着写了个小例子,现在也把学到的一些东西记录下来,供以后查阅,属于初级入门的范围,望共勉。   进程是系统中程序执行和分配资源的基本单位,每个进程都有自己独立的数据段,代码段和堆栈段。而线程是系统中独立运行的最小单位,可以说操作系统分配CPU时间的最小单位,可以叫轻型的进程。一个进程可以有多个线程,在多进程情况下,每个进程都有着自己的地址空间,

月子G 的专栏 1万+

USB2.0学习1--基本概念

USB全称是 Universal Serial Bus,即通用串行总线,是一种计算机与外围设备进行数据交互的串行通信标准。USB协议是在1994年底由英特尔、IBM、Microsoft等公司联合提出的,现由USB协会(USB-IF)管理。USB使用差分信号来传输数据,具有传输速度快、支持热插拔、连接灵活、独立供电、协议向下兼容等优点,目前应用十分广泛。

原海青木的博客 3758

通过InoDriverShop伺服调试软件连接汇川SV660F系列伺服的具体方法示例

通过InoDriverShop伺服调试软件连接汇川SV660F系列伺服的具体方法示例

Robot_PLC_HMI_Automation 1万+

Linux —— 多线程

目录一、线程的概念1.理解线程 2.线程的优点3.线程的缺点 4.线程异常5.线程用途二、Linux进程VS线程 1.进程和线程2.进程的多个线程共享3.进程和线程的关系三、Linux线程控制1.POIX线程库2.线程创建3.线程等待4.线程终止5.分离线程6.线程ID及进程地址空间布局 如何理解之前的进程?CPU如何看待task_struct?Linux下并不存在真正的线程?而是用进程模拟的?......

M的博客 7077

Python库CloudScraper详细使用(绕过 Cloudflare 的反机器人页面的 Python 模块)

CloudScraper是一个专门用于绕过Cloudflare反机器人保护的Python库,它基于requests库构建,能够处理Cloudflare的5秒盾、WAF和人机验证等防护机制。

数据知道的博客 1万+

【Linux | C++ 】生产者消费者模型(Linux系统下C++ 代码模拟实现)

生产者消费者模型是多线程编程中的一个经典案例,它涉及到两类线程:生产者和消费者。生产者线程不断地生成数据,并将数据放入一个缓冲区中,消费者线程则不断地从缓冲区中获取数据,并进行相应的处理。在这个过程中,为了保证数据的完整性和正确性,需要使用同步机制来协调两类线程之间的操作,以避免数据竞争和死锁等问题的发生。本文将介绍如何使用条件变量来实现生产者消费者模型,帮助读者更好地理解多线程编程中的同步机制和技术。

Yawesh的博客 3849

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Linux生产者消费者模型

文章目录生产者消费者模型为何要使用生产者消费者模型生产者消费者模型优点基于BlockingQueue的生产者消费者模型C++queue模拟阻塞队列的生产消费模型POSIX信号量POSIX信号量函数基于环形队列的生产消费模型 生产者消费者模型 为何要使用生产者消费者模型 生产者消费者模型优点 基于BlockingQueue的生产者消费者模型 C++queue模拟阻塞队列的生产消费模型 POSIX信号量 POSIX信号量函数 基于环形队列的生产消费模型 ...

2021dragon的博客 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【Linux】生产者消费者模型

🍁 🍃 🍂 🌿。

m0_60338933的博客 1901

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

linux:生产者消费者模型

本文是对于生产者消费者模型的知识总结以上就是我对于线程同步的总结。

li209779的博客 2763

【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)

在本文中,我们将详细探讨基于环形队列的生产者消费者模型的设计和实现。我们将介绍环形队列的数据结构,分析生产者和消费者之间的同步机制,探索如何利用前文提到的POSIX信号量以及其他同步工具(如互斥锁)来实现生产者和消费者之间高效、安全的数据交换。通过具体的代码示例和案例分析,读者将能够深入理解生产者消费者模型的工作原理,掌握如何在实际项目中设计和实现基于环形队列的高效同步模型

Yawesh的博客 2192

『Linux』第九讲:Linux多线程详解(四)_ 生产者消费者模型

生产者消费者模型详解

枫叶先生的博客 2051

【Linux多线程】生产者消费者模型

本篇博客将会介绍Linux多线程中一个非常重要的模型——生产者消费者模型

m0_74215144的博客 1612

Linux生产者消费者模型(31)

Hello,我们继续来学习Linux!!!终于马上就能结束系统了!欧耶!再撑一下,Linux的网络部分终于要结束了!

tan180°的博客 1523

【Linux】线程同步与互斥 (生产者消费者模型)

详细讲解了linux线程同步互斥与生产消费模型 附加了大量的代码实例

You build it, you run it 8884

【Linux】基于环形队列的生产者消费者模型的实现

上一篇文章我们讲了信号量的几个接口和基于环形队列的生产者消费者模型,下面我们就快速来实现。

Sxy_wspsby的博客 1100
上一篇: Redis内存回收:LRU算法
下一篇: CodeBlocks链接时报未找到错误 undefined reference to
lycommand
博客等级 码龄14年 31粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值