线程同步--生产者消费者模型

news2024/12/30 3:20:37

在这里插入图片描述

文章目录

  • 一.条件变量
    • pthread线程库提供的条件变量操作
  • 二.生产者消费者模型
    • 生产者消费者模型的高效性
    • 基于环形队列实现生产者消费者模型中的数据容器

一.条件变量

  • 条件变量是线程间共享的全局变量,线程间可以通过条件变量进行同步控制
  • 条件变量的使用必须依赖于互斥锁以确保线程安全,线程申请了互斥锁后,可以调用特定函数进入条件变量等待队列(同时释放互斥锁),其他线程则可以通过条件变量在特定的条件下唤醒该线程(唤醒后线程重新获得互斥锁),实现线程同步.
    • 例如一个线程访问队列时,发现队列为空,则它只能等待其它线程将数据添加到队列中,这种情况就需要用到条件变量.
    • 线程同步的概念:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问共享资源,从而有效避免线程饥饿问题(饥饿问题指线程长时间等待资源而无法被调度).
      在这里插入图片描述

pthread线程库提供的条件变量操作

//声明全局互斥锁并初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//声明全局条件变量并初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 线程等待条件:
任务线程代码{
	pthread_mutex_lock(&mutex);
	if(条件为假)
		pthread_cond_wait(&cond, &mutex);//等待时会释放互斥锁,等待完后自动加锁
	//访问共享资源....
	pthread_mutex_unlock(&mutex);
}

线程调用pthread_cond_wait等待时,该接口会释放互斥锁,等待结束后自动加锁

  • 控制线程给条件变量发送唤醒信号
控制线程代码{
	if(满足唤醒条件){
		pthread_mutex_lock(&mutex);
		pthread_cond_signal(cond);
		pthread_mutex_unlock(&mutex);
	}
}

唤醒操作加锁是为了避免信号丢失

  • 示例:
#include <iostream>
#include <unistd.h>
#include <pthread.h>

int cnt = 0;
//声明全局互斥锁并初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//声明全局条件变量并初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *Count(void * args)
{
    //线程分离,无需主线程等待
    pthread_detach(pthread_self());
    uint64_t number = (uint64_t)args;
    std::cout << "pthread: " << number << " create success" << std::endl;

    while(true)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);               
        std::cout << "pthread: " << number << " , cnt: " << cnt++ << std::endl;
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    for(uint64_t i = 0; i < 4; i++)
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, Count, (void*)i);
        usleep(1000);
    }
    sleep(3);
    std::cout << "main thread ctrl begin: " << std::endl;

    while(true) 
    {
        sleep(1);
        //唤醒在cond的等待队列中等待的一个线程,默认都是第一个
        pthread_mutex_lock(&mutex);
        pthread_cond_signal(&cond); 
        pthread_mutex_unlock(&mutex);
        //按顺序唤醒在cond的等待队列中的所有线程
        //pthread_cond_broadcast(&cond);
        std::cout << "signal one thread..." << std::endl;
    }

    return 0;
}
  • 线程同步过程图解:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 条件变量和锁的销毁:
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);

二.生产者消费者模型

  • 生产者消费者模型是一种多线程并发协作的设计框架,生产者负责生成并发送数据,消费者负责接收并处理数据.
  • 生产者和消费者之间存在一个数据容器作为缓冲区,生产者生产的数据存入容器中,消费者需要的数据从容器中获取,实现了生产者和消费者之间的数据传输解耦
  • 数据容器由互斥锁保护,同一个时刻只能有一个线程访问数据容器,生产者和消费者之间通过条件变量(或信号量)实现同步
  • 对于数据容器的访问,生产者和消费者遵循三个原则:
    • 生产者和生产者之间互斥
    • 消费者和消费者之间互斥
    • 生产者和消费者之间互斥并同步
      在这里插入图片描述

生产者消费者模型的高效性

  • 由于生产者和消费者之间的数据传输解耦,生产者生产完数据之后不用等待消费者处理数据,而是直接将数据存入容器,消费者不需要向生产者请求数据,而是直接从容器里获取数据,因此即便在生产者和消费者的效率不对等且多变的情况下,多个生产者依然可以高效专一地并发生产数据,多个消费者依然可以高效专一地并发处理数据,使得系统整体的并发量得到提高
    在这里插入图片描述

基于环形队列实现生产者消费者模型中的数据容器

  • 环形队列中,消费者访问队列的头指针进行数据出队操作,生产者访问队列的尾指针进行数据入队操作
  • 两把互斥锁分别保证消费者和消费者之间的互斥以及生产者和生产者之间的互斥,两个信号量实现消费者和生产者之间的互斥与同步
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 当环形队列既不为空也不为满时,支持一个生产者和一个消费者并发地进行数据的存取
#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

//环形队列默认容量
const static int defaultcap = 5;

template<class T>
class RingQueue{
private:
    std::vector<T> ringqueue_;
    int cap_;          //容器的容量

    int c_step_;       // 消费者环形队列指针
    int p_step_;       // 生产者环形队列指针

    sem_t cdata_sem_;  // 消费者的数据资源
    sem_t pspace_sem_; // 生产者的空间资源

    pthread_mutex_t c_mutex_;   //消费者与消费者之间的互斥锁
    pthread_mutex_t p_mutex_;   //生产者与生产者之间的互斥锁
public:
    RingQueue(int cap = defaultcap)
        :ringqueue_(cap), cap_(cap), c_step_(0), p_step_(0)
    {
        //初始化生产者和消费者的信号量-->消费者一开始没有信号量资源,生产者一开始具有最多的空间资源
        sem_init(&cdata_sem_, 0, 0);
        sem_init(&pspace_sem_, 0, cap);

        pthread_mutex_init(&c_mutex_, nullptr);
        pthread_mutex_init(&p_mutex_, nullptr);
    }
    ~RingQueue()
    {
        sem_destroy(&cdata_sem_);
        sem_destroy(&pspace_sem_);

        pthread_mutex_destroy(&c_mutex_);
        pthread_mutex_destroy(&p_mutex_);
    }

    //信号量的资源状态可以区分队列的空和满

    void Push(const T &in) 
    {
        //生产者等待空间资源
        sem_wait(&pspace_sem_);
        pthread_mutex_lock(&p_mutex_);
        ringqueue_[p_step_] = in;
        p_step_++;
        p_step_ %= cap_;
        pthread_mutex_unlock(&p_mutex_);
        //生产完数据后增加消费者的信号量资源
        sem_post(&cdata_sem_);
    }
    void Pop(T *out)      
    {
        //消费者等待数据资源
        sem_wait(&cdata_sem_);
        pthread_mutex_lock(&c_mutex_);
        *out = ringqueue_[c_step_];
        c_step_++;
        c_step_ %= cap_;
        pthread_mutex_unlock(&c_mutex_);
        //消费完数据后增加生产者的信号量资源
        sem_post(&pspace_sem_);
    }
};

在这里插入图片描述

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

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

相关文章

【C语言】- 设置控制台文字颜色、大小和字体

【C语言】- 设置控制台标题、编码、文字颜色、大小和字体 文章目录 【C语言】- 设置控制台标题、编码、文字颜色、大小和字体1 - 设置控制台标题2 - 设置控制台编码3 - 设置控制台字体和大小参考链接 1 - 设置控制台标题 因为要用到 Windows API&#xff0c;所以需要包含头文件…

CHAPTER 9: 《DESIGN A WEB CRAWLER》第9章 《设计一个web爬虫》

CHAPTER 9: 《DESIGN A WEB CRAWLER》第九章 设计一个web爬虫 在本章中&#xff0c;我们将重点介绍网络爬虫设计&#xff1a;一种有趣而经典的系统设计 面试问题。 网络爬虫被称为机器人或蜘蛛。它被搜索引擎广泛用于发现网络上的新内容或更新内容。内容可以是网页、图像、视频…

python:一元线性回归模型案例分析

一、案例分析背景 案例: 中国全体居民的消费水平与经济发展数量关系的分析 提出问题&#xff1a; 改革开放以来&#xff0c;随着中国经济的快速发展&#xff0c;人民生活水平不断提高&#xff0c;居民的消费水平也在不断增长。研究中国全体居民的消费水平与经济发展的数量关系…

代码随想录算法训练营第31天 | 理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和

目录 理论基础 455.分发饼干 &#x1f4a1;解题思路 &#x1f4bb;实现代码 376. 摆动序列 &#x1f4a1;解题思路 # 情况一&#xff1a;上下坡中有平坡 # 情况二&#xff1a;数组首尾两端 情况三&#xff1a;单调坡度有平坡 &#x1f4bb;实现代码 53. 最大子序…

RTC讲解

RTC&#xff08;Real Time Clock&#xff09;实时时钟 RTC实时时钟本质上是一个独立的定时器。RTC模块拥有一组连续计数的32位无符号计数器&#xff0c;在相应软件配置下&#xff0c;可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配…

uniapp APP接入Paypal

1. 登录paypal开发者中心&#xff0c; 2. 选择 Apps & Credentials 点击 Create App创建应用&#xff0c;创建后点击编辑按钮&#xff0c;如图&#xff1a; 3. 进入应用详情&#xff0c;勾选Log in with PayPal点击 Advanced Settings 添加return URL等信息并保存。如图&a…

C++(13)——string

上篇文章中介绍了中部分函数的用法&#xff0c;本篇文章将继续对其他的函数进行介绍&#xff1a; 1. substr: string substr (size_t pos 0, size_t len npos) const; 函数的两个参数如上述代码所示&#xff0c;此函数的主要作用是根据一个已有的的对象的起始坐标开始&a…

快手二面:节流和防抖知道吗?

面试官:节流与防抖&#xff0c;说说两者各自适用场景&#xff1f; 回答&#xff1a;面试官&#xff0c;在前端开发中&#xff0c;节流&#xff08;Throttle&#xff09;和防抖&#xff08;Debounce&#xff09;是两种常用的优化高频率执行JavaScript代码的技术。我将分别阐述它…

ICC2:channel power plan/power switch的添加方法

更多学习内容请关注「拾陆楼」知识星球 拾陆楼知识星球入口 往期文章链接: low power与pg strategy (lib cell pin connect) low power与pg strategy(pg mesh) low power与pg

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)。 在软件行业,对于什么是架构,都有很多的争论,每个人都有自己的理解。在不同的书籍上, 不同的作者, 对于架构的定义也不统一, 角度不同, 定义不同。 一、架构是什么 Linux 有架构,MySQL 有架构,J…

关于接口的安全性测试,这方法你学会了吗?

01、接口防刷 1.为什么会有人要刷接口&#xff1f; 牟利&#xff1a;黄牛在 12306 网上抢票再倒卖。 恶意攻击竞争对手&#xff1a;如短信接口被请求一次&#xff0c;会触发几分钱的运营商费用&#xff0c;当量级大了也很可观。 压测&#xff1a;用apache bench 做压力测试…

鸿蒙开发实战-(ArkUI)List组件和Grid组件的使用

在我们常用的手机应用中&#xff0c;经常会见到一些数据列表&#xff0c;如设置页面、通讯录、商品列表等。下图中两个页面都包含列表&#xff0c;“首页”页面中包含两个网格布局&#xff0c;“商城”页面中包含一个商品列表。 上图中的列表中都包含一系列相同宽度的列表项&am…

python使用贪心算法求最大整数问题

对于使用贪心算法的一个比较经典的问题&#xff0c;主要是为了解决最大整数的拼接问题&#xff0c;如果给定一个列表&#xff0c;这个列表中所包括的是一些非负整数&#xff0c;如果对这些整数进行组合&#xff0c;怎样才能组合出一个最大的整数&#xff0c;这里要注意一个问题…

自动派单系统哪家好?实现自动派单对于管理有什么帮助?

随着科技的发展&#xff0c;自动化管理已成为各行各业追求效率与精准的关键。在维修服务领域&#xff0c;自动派单系统的出现为管理员和用户带来了前所未有的便捷。通过自动匹配维修人员、实时通知用户进度等功能&#xff0c;自动派单系统不仅提高了派单的准确性和效率&#xf…

django后台进行加密手机号字段,加密存储,解密显示

需求: 1 &#xff1a;员工在填写用户的手机号时&#xff0c;直接填写&#xff0c;在django后台中输入 2&#xff1a;当员工在后台确认要存储到数据库时&#xff0c;后台将会把手机号进行加密存储&#xff0c;当数据库被黑之后&#xff0c;手机号字段为加密字符 3&#xff1a;员…

Python基础第一篇(Python概念介绍)

文章目录 一、前言&#xff1a;二、第一个Python程序三&#xff0c;理解Python的解释器四&#xff0c;Python解释器的使用五&#xff0c;Python开发环境 一、前言&#xff1a; 欢迎来到我们的Python学习专栏。在这里&#xff0c;我们将一起探索Python这门强大、灵活、易于学习…

微信小程序+前后端开发学习材料2-(视图+基本内容+表单组件)

学习来源 视图 1.swiper 滑块视图容器。其中只可放置swiper-item组件&#xff0c;否则会导致未定义的行为。 显示面板指示点indicator-dots 基础内容 1.icon 图标组件 实例演示 2.progress 进度条。组件属性的长度单位默认为px&#xff0c;咱用rpx。 实例演示 这…

SVO编译

文章目录 软件版本错误编译运行轨迹路径保存运行TUM数据集 附录针对svo slam的/svo/pose_imu转为tum格式代码 软件版本 ubuntu 20 rosnoeticSVO SLAM虚拟机 windows 11 错误 常见的git clone问题可以使用DevSidecar解决&#xff0c;在 加速服务-基本设置-绑定IP 设置为0.0.0…

Cinder对接NFS文件系统实验详解

2、对接NFS&#xff08;Network File System&#xff1a;网络文件系统&#xff09;实验 实验前的准备&#xff1a; 完整克隆一台虚拟机作为Cinder的后端存储&#xff08;IP为192.168.100.40&#xff09; Cinder节点作为服务端&#xff0c;把Controller作为客户端 Controlle…

git仓库使用说明

Git软件使用 1.先下载git相关软件 下载地址&#xff1a; Git - Downloading Package (git-scm.com) 下载其中一个安装 2.打开gitee网站&#xff0c;注册账号 3.打开个人中心&#xff0c;选择ssh公钥&#xff0c;查看如何生成公钥 4.生成公钥后&#xff0c;添加相应的公钥 …