C语言多线程_生产者与消费者模型_链式队列实现

多线程实现生产者与消费者模式 生产者-消费者模式的简介:在实际的软件开发过程中,我们将产生数据的模块称为生产者,处理数据的模块成为消费者。但仅有这两者还不够成为一个生产者-消费者模式,还需要有一个缓冲区(一段内存区域)作为中介,生产者产生的数据放入缓冲区,消费者从缓冲区读取数据并处理。(注:上述所说的模块是广义的,可以是类,函数,线程,进程等)我们可以将这二者之间的关系图表示出来:总结:我们用3-2-1的方法来简单描述一个生产者 阅读详情

代码中有详细注释,详见readme.txt

这个主要是基于条件变量和互斥量来实现,如果缓存队列在没有满或者没有空的场景下,读写线程只需要在数据插入删除的时候持有锁,使用完释放锁就可以保证原子性。
当进入读写线程发现队列满或者空的时候,就需要使用条件变量把线程阻塞,并释放锁,当写线程插入数据后,就会发一个不空的信号去唤醒读线程。反之同理。

相关API

线程
pthread_create 传入线程id创建线程
pthread_join 主线程阻塞等待指定线程结束

初始化
pthread_mutex_init 初始化锁
pthread_cond_init 初始化条件变量

pthread_cond_wait 前要先加锁
pthread_cond_wait 内部会解锁,然后等待条件变量被其它线程激活
pthread_cond_wait 被激活后会再自动加锁

pthread_mutex_lock 加锁(读写线程用同一个锁)
pthread_mutex_unlock 解锁
pthread_cond_signal 发送信号

cmakelist

cmake_minimum_required(VERSION 3.17)
project(A)

set(CMAKE_CXX_STANDARD 14)


add_executable(A  fun.cpp final.cpp)
#add_executable(A queue.c)

find_package(Threads REQUIRED)
target_link_libraries(A Threads::Threads)

fun.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "final.h"
#include <unistd.h>
#include<pthread.h>


struct Queue *init_queue()
{
    struct Queue *queue=(struct Queue *)malloc(sizeof(struct Queue));
    if(!queue)
    {

        exit(1);
    }


    queue->front=(struct Node*)malloc(sizeof(struct Node));

    queue->front->next=NULL;
    queue->rear=queue->front;
    queue->len=0;
    queue->max_len=MAX_LEN;
    pthread_mutex_init(&queue->q_lock,NULL);
    pthread_cond_init(&queue->not_empty,NULL);
    pthread_cond_init(&queue->not_full,NULL);


    return queue;
}


void en_queue(struct Queue *q,void* data,size_t len)//向队列的尾部插入一个数据,请传入数据类型的长度
{

    pthread_mutex_lock(&q->q_lock);

    while(q->len==MAX_LEN)
    {
       // printf("this queue is full\n");//这句使用是可以注释掉,会等待读函数删掉后再继续写
        pthread_cond_wait(&q->not_full,&q->q_lock);

    }
    struct Node *newnode=(struct Node*)malloc(sizeof(struct Node));

       newnode->data=malloc(len);
    memcpy(newnode->data,data,len);


    newnode->next=NULL;
    q->rear->next=newnode;
    q->rear=newnode;
    q->len=q->len+1;

    pthread_cond_signal(&q->not_empty);
    pthread_mutex_unlock(&q->q_lock);
}
void de_queue(struct Queue *q)//删除头部一个节点和他的数据域
{
    pthread_mutex_lock(&q->q_lock);

    while(q->len==0)
    {
        printf("this queue is empty");
        pthread_cond_wait(&q->not_empty,&q->q_lock);
    }

    struct Node *newnode;

    newnode=q->front->next;


    q->front->next=newnode->next;
    if(q->rear==newnode)
        q->rear=q->front;
    free(newnode);
    q->len=q->len-1;

    pthread_cond_signal(&q->not_full);
    pthread_mutex_unlock(&q->q_lock);
    return ;
}

int read_queue_save(struct Queue *q,void * out_space,size_t size_len)//传入数据类型的大小  读取数据并且保存,
{

    pthread_mutex_lock(&q->q_lock);

 //   printf("q len is %d\n",q->len);
    while(q->len==0)
    {
  //      printf("this queue is empty\n");
        pthread_cond_wait(&q->not_empty,&q->q_lock);

    }


    struct Node *read_node;
    read_node=q->front->next;
    int i=0;
    if(read_node==NULL)
    {
        printf("this queue is empty\n");
        return -1;
    }

    memcpy(out_space,read_node->data,size_len);


 //   read_node=read_node->next;
    pthread_cond_signal(&q->not_empty);
    pthread_mutex_unlock(&q->q_lock);
    return 0;
}
int destroy_queue(struct Queue *q)
{
    while(q->front)
    {
        q->rear=q->front->next;
        free(q->front);
        q->front=q->rear;
    }
    printf("destroy_queue ok\n");
    return 0;
}

final.h

#ifndef _QUEUE_H_
#define _QUEUE_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#ifdef __cplusplus
extern "C"
{
#endif

#define MAX_LEN 100 //在这里定义数据的最大的长度

struct Node {
    void* data;
    // mytype data;
    struct Node *next;
};

struct Queue {
    struct Node *front;
    struct Node *rear;
    int len;
    int max_len;

    pthread_mutex_t q_lock;
    pthread_cond_t not_empty;
    pthread_cond_t not_full;

};


struct Queue *init_queue();
void en_queue(struct Queue *q,void* data,size_t len);//向队列的尾部插入一个数据
void de_queue(struct Queue *q);//删除头部一个节点和他的数据域
int read_queue_save(struct Queue *q,void * out_space,size_t len);//读取数据并且保存在out_space,不删除
int create_pipe();
int destroy_queue(struct Queue *q);

#ifdef __cplusplus
}
#endif

#endif

final.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "final.h"
#include <unistd.h>

typedef struct DATA1{
int ID;
char sex[16];
char name[26];

}data1;//模拟数据来源

typedef struct DATA2{
    int ID;
    char sex[16];
    char name[26];
}data2;//模拟数据接收空间

struct parameter//用户可定义更多的参数
{
    int fd;
    struct Queue *q;
};

struct parameter *par1=new parameter;
int create_pipe()
{
    par1->q=init_queue();
    if(par1->q!=NULL)
    {
        par1->fd=1;
        return par1->fd;
    }
    else
    {
        par1->fd=-1;
        return par1->fd;
    }

}

void *write_fn_ky(void *arg)
{
    struct parameter *p=(parameter*)arg;
    data1 da1;
    strcpy(da1.sex,"man");
    strcpy(da1.name,"good morning");
    printf("1111\n");
    for(int i = 0 ;i < 1000000; i++)
    {
        da1.ID = i;
        en_queue(par1->q, (void *) &da1, sizeof(da1));
        sleep(1);//模拟的写数据频率
    //    printf("1111\n");

    }



    return ((void *)0);


}
void *read_fn_ky(void *arg)
{

    struct parameter *p=(parameter*)arg;
    data1 da2;
    while(1)
    {

        int i;
        sleep(1);//模拟的读数据的频率
        //i = pipe_read(fd, (void*)& da2 , sizeof(da2))
        //int e;
        i=read_queue_save(par1->q,(void*)& da2 , sizeof(da2));
        if(i == 0)
        {
            printf("read success ID = %d , sex = %s , name = %s\n",da2.ID,da2.sex,da2.name);
            de_queue(par1->q);
            //finish_del(fd);
        }
        else
        {
            printf("read error\n");
        }
    }
    return ((void *)0);

}
int main()
{

    int fd= create_pipe();
    if(fd<0)
    {
        printf("=========测试1功能一,创建失败===========\n");
        return  -1;
    }
    printf("=========测试2功能二,进行正常读写===========\n");

    pthread_t id_1;
    pthread_t id_2;
    int err1;
    int err2;

    err1=pthread_create(&id_1,NULL,write_fn_ky,(void *)par1);//若成功返回0
    if(err1!=0)
    {
        printf("create write_fn_ky error\n");
        exit(1);
    }

    err2=pthread_create(&id_2,NULL,read_fn_ky,(void *)par1);
    if(err2!=0)
    {
        printf("create read_fn_ky error\n");
        exit(1);
    }

    pthread_join(id_1,NULL);
    pthread_join(id_2,NULL);


}


readme.txt

author:KQZ
date:2020-10-28

本工程用cmakelist代替makelife的作用


//
fun.cpp中主要为队列读写功能的实现

struct Queue *init_queue();//初始化
void en_queue(struct Queue *q,void* data,size_t len);//向队列的尾部插入一个数据。
void de_queue(struct Queue *q);//删除头部一个节点和他的数据域
int read_queue_save(struct Queue *q,void * out_space,size_t len);//读取数据并且保存在out_space,不删除,这里设计的目的是为了数据处理失败时能够重新读取数据
int create_pipe();//初始化,返回fd
int destroy_queue(struct Queue *q);//销毁

//
final.cpp 主函数 测试实例
void *write_fn_ky(void *arg)//示例写函数
void *read_fn_ky(void *arg)//示例读函数

利用多线程中的互斥量和条件变量实现读写的原子性问题

用户可以通过改变宏定义来决定队列最大的长度#define MAX_LEN 100
用户可以通过改变write_fn_ky read_fn_ky 中的sleep()来模拟读写的频率
当队列满的时候,写函数会等待读函数发出pthread_cond_t not_full;
当队列空的时候,读函数会等待写函数发出pthread_cond_t not_empty;

//测试用例
typedef struct DATA1{
int ID;
char sex[16];
char name[26];

}data1;//模拟数据来源
typedef struct DATA2{
    int ID;
    char sex[16];
    char name[26];
}data2;//模拟数据接收空间






多线程实验_多生产者多消费者操作一个栈list //只是很简单的实现了数据集,数据集的操作服务类,多线程模拟多生产者和多消费者。最后一个测试类。 多生产者多消费者。 阅读详情

相关推荐

Linux C语言线程池实现:从生产者消费者模型到工业级优化

在并发编程中,生产者消费者模型是解决任务调度与资源管理的经典范式,其核心原理是通过共享缓冲区解耦生产与消费过程,从而提升系统吞吐量与资源利用率。这一模型的技术价值在于能够有效避免线程频繁创建销毁的开销,减少上下文切换,是构建高性能服务器的基石。其典型应用场景包括Web服务器请求处理、日志分析、数据并行计算等需要处理大量独立短任务的场合。本文聚焦于Linux环境下使用C语言与POSIX线程库实现一个固定大小的线程池,深入探讨了基于互斥锁和条件变量的同步机制、优雅关闭策略以及避免惊群效应等关键实现细节,并进一步

cuililai5127的博客 375

C++多线程实现生产者消费者模式

这里记录C++多线程实现生成者消费者模式。 代码执行环境:wins Visual Studio 2017 执行逻辑:生产者和消费者公用一个队列作为临界资源,当队列满时生产者暂停生成;当队列为空时,消费者暂停消费。使用锁来保证队列中数据的一致性。 执行结果截图: #include<iostream> #include<vector> #include<thread> #include<queue> #include<mutex> #include&

ChainingBlocks 2776

C语言实现四大经典数据结构库(队列、栈、链表、树)增删改查实战

简介:数据结构是计算机科学的核心,关乎数据的高效组织与操作。本资料包提供一套完整的C语言数据结构库,涵盖队列、栈、链表和树四种基础结构的增删改查功能,深入运用指针、动态内存分配、结构体等C语言核心机制。适用于任务调度、表达式求值、文件系统模拟等场景,支持Linux环境下的程序调用,提升代码复用性与程序性能。通过本库实践,开发者可深入理解数据结构底层原理,强化内存管理能力,避免内存泄漏与悬挂指针问题。

weixin_31459297的博客 954

安全函数不安全-多线程慎用List.h

linux开发应该多少都听过大名鼎鼎的 list.h ,其简洁优雅的设计,一个头文件完成了一个高可用的链表。 前言 linux 开发应该多少都听过大名鼎鼎的 list.h ,其简洁优雅的设计,一个头文件完成了一个高可用的链表。 但是 list.h 并不是线程安全的,在多线程的情况下使用,必须考虑多线程数据同步的问题。 然而。。。。 我在使用互斥锁对链表的操作进行保护之后,还是被坑了! 下面是把我坑了的 list_for_each_entry 和 list_for_each_entry_s.

Free雅轩的博客 674

LBM模式学习·保姆级安装及初步使用教程

在学习LBM的过程中,我感觉可供参考的材料较为有限,对于一个独立学习LBM模式的初学者非常不友好(我就是这个初学者哈哈),因此萌生了写一篇保姆级的LBM模式教程,并将我踩过的坑一一指出,有助于后来者更快地上手该模式,而非半途而废。

weixin_42762673的博客 1万+

告别Keil?在Mac上配置STM32CubeIDE + CMSIS-DAP调试器全攻略(含常见报错解决)

本文详细介绍了在Mac平台上配置STM32CubeIDE与CMSIS-DAP调试器的全流程,包括硬件准备、软件安装、工程创建及调试优化。通过实战指南,帮助开发者高效完成STM32开发,解决常见报错问题,提升开发效率。

weixin_30485379的博客 433

c语言多线程生产者消费者,C++生产者与消费者多线程样例

点击上方蓝字可直接关注!方便下次阅读。如果对你有帮助,麻烦点个在看或点个赞,感谢~文章首发公众号—— Pou光明先了解问题背景:生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生...

weixin_32050033的博客 1255

sysbench安装(在线&离线)

sysbench下载、安装、编译和异常处理方法

m0_61066945的博客 8756

链式队列:从数据结构基础到消息队列核心原理的C语言实现

队列是一种遵循先进先出(FIFO)原则的基础数据结构,广泛应用于任务调度、数据缓冲等场景。其核心原理在于维护元素的顺序访问,通过队头和队尾指针高效管理入队与出队操作。在技术实现上,链式队列利用动态内存分配,克服了数组队列固定容量的限制,提供了更好的内存利用率和弹性伸缩能力,这对于构建高性能、可扩展的系统至关重要。在工程实践中,链式队列是理解更复杂系统(如消息队列)的基础模型,常用于实现生产者-消费者模式、广度优先搜索(BFS)算法以及网络数据包缓冲等。本文以C语言为例,详细拆解了链式队列的结构设计、核心操作

weixin_32247165的博客 257

168、OIS光学防抖系统设计:陀螺仪标定、音圈马达驱动与滚珠式/悬丝式结构选型

本文总结了OIS光学防抖系统设计中的关键问题与解决方案。作者通过实际案例指出,陀螺仪标定需重视零偏、灵敏度和温度补偿问题,音圈马达驱动需采用电流环控制并优化相位补偿。在机械结构选型方面,滚珠式适合大角度补偿但存在回差问题,悬丝式精度高但受重力影响,需根据应用场景选择。系统联调建议从开环到闭环逐步验证,注意陀螺仪与驱动频率匹配。最后给出5条经验建议:关注陀螺仪信噪比而非分辨率、预留标定余量、重视机械结构公差、配备专业调试工具、协调OIS与电子防抖的分工配合。文章强调OIS调试需针对不同模组特性进行反复测试与标

zzfmlxs的博客 46

【全覆盖算法】栅格地图实现布氏单元分解算法+栅格地图实现牛耕Boustrophedon Cellular Decomposition Path Planning

本文先对做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容。

qq_35635374的博客 282

C语言实现队列

队列是一种特殊的线性数据结构,遵循先入先出(FIFO)的原则。它只允许在队列的末尾添加元素(称为入队操作),并从队列的开头移除元素(称为出队操作)。队列在多种应用中发挥着重要作用,如计算机系统的任务调度、打印机作业管理以及多线程编程中的线程同步等。

qq_74013365的博客 1940

ABAP 7.40 新语法介绍系列之六:使用 FILTER 关键字对 ABAP 内表进行过滤

笔者之前写过的关于 ABAP 740 新语法的文章:本文介绍另一个新的关键字:FILTER.FILTER 是 ABAP 740 中引入的一个关键字,用于简化数据操作,它能够将内部表(Internal Table)中的数据按特定的条件进行过滤,并生成一个新的内部表。FILTER 的引入旨在使代码更加现代化和高效,尤其是在面对大量数据需要过滤的情况下。传统上,开发人员可能需要使用 LOOP AT 结合条件判断来实现,但使用 FILTER 关键字后,代码显得更加简洁且易于维护。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1171

面试官最爱问的链式队列如何写?一文看懂C语言完整实现

掌握C语言实现队列的链式存储结构,轻松应对面试高频考点。本文详解节点定义、入队出队操作及内存管理,适用于动态数据处理场景,具有高效灵活的优势。代码完整可运行,值得收藏。

CompiGlow的博客 630

【四足机器人项目实战】四足机器人运动学、动力学模型

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、四足机器人实际模型的物理难点二、四足机器人运动学模型1.方法一:DH法建立运动学模型(1)运动学建模构型方法(2)运动学模型描述方法1、基于DH法四足机器人单腿运动学模型公式推导(0)DH法科普1)原理2)实现过程3)评价(1)四足机器人的建模规则、坐标系定义及实物参数确定1)标准Denavit-Hartenberg法建模规则2)单腿运动学模型坐标系定义示意图及简化模型3)

qq_35635374的博客 1万+

【时时三省】vector cast 常见错误解决/vcast常见错误解决

介绍在使用 vector cast/vcast 软件的过程中,遇到的异常问题。其它异常可以私信解决(购买专栏的前提下)。

时时三省的博客 450

【高性能并发编程必修课】:深入剖析C语言链式队列的线程安全实现机制

掌握C语言链式队列的并发安全操作,解决多线程环境下的数据竞争与一致性难题。适用于高并发服务器、任务调度等场景,结合互斥锁与原子操作实现高效线程安全,提升系统稳定性与性能表现。方法实用可靠,值得收藏。

FuncLens的博客 1076

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

为什么你的队列总是内存泄漏?C语言链式存储避坑指南

掌握C语言实现队列的链式存储结构,有效避免内存泄漏难题。本文详解节点动态分配与释放机制,适用于多任务缓存、消息队列等场景。提供完整代码示例与销毁函数实现,确保资源安全回收,提升程序稳定性,值得收藏。

InstrFun的博客 686

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【C语言链式队列并发安全实战】:掌握高并发场景下的线程安全操作秘技

掌握C语言链式队列的并发安全操作,解决多线程环境下的数据竞争问题。适用于高并发服务器、任务调度等场景,结合互斥锁与原子操作实现高效线程安全,提升系统稳定性与性能表现,值得收藏。

AlgoChat的博客 808

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【C语言链式队列并发安全实战】:掌握高并发场景下的线程安全队列设计精髓

掌握C语言链式队列的并发安全操作,解决多线程环境下的数据竞争问题。适用于高并发服务器、任务调度等场景,通过互斥锁与原子操作实现线程安全,保障队列高效稳定。结构清晰、扩展性强,值得收藏。

ProcePerch的博客 1083

【高性能编程必修课】:深入剖析C语言链式队列的原子操作与内存屏障

掌握高并发场景下C语言链式队列的并发安全操作,详解原子操作与内存屏障实现机制。适用于多线程环境下的高效数据通信,保障线程安全与性能平衡,提升系统稳定性。方法实用,值得收藏。

ProceShoal的博客 1072

如何让C语言链式队列支持百万级并发?99%开发者忽略的内存屏障细节

掌握C语言链式队列的并发安全操作,轻松应对百万级高并发场景。通过原子操作与内存屏障优化,解决多线程竞争问题,提升性能与稳定性,适用于高性能服务器与实时系统开发,值得收藏。

DeepLens的博客 1009

动态内存管理+指针操作实战,深度剖析C语言链式队列实现

掌握C语言实现队列的链式存储结构,解决动态数据高效管理难题。结合动态内存分配与指针操作,详解入队出队逻辑,适用于任务调度、缓冲处理等场景,提升程序灵活性与性能,值得收藏。

FastCompile的博客 880

6.c语言实现队列【由浅入深-数据结构】

本文介绍c语言实现队列的相关内容。

插花弄玉的博客 1110

实时系统中的队列优化方案(C语言循环数组实现精髓)

解决实时系统数据高效缓存难题,本文深入剖析C语言实现队列的循环数组结构,适用于嵌入式与高并发场景,具备内存利用率高、出入队操作O(1)等优势,代码简洁可靠,值得收藏。

LogicGlow的博客 887

队列详解:定义、分类、应用场景及与栈的区别(C/C++ 代码讲解)

本文介绍队列(先进先出,FIFO)及其多种变体:顺序/循环队列(数组实现,解决假溢出)、链式队列(动态增长)、双端队列(两端操作)、优先队列(按优先级出队)、阻塞队列(线程安全,用于生产者-消费者模型)。核心操作:入队(enqueue)、出队(dequeue)。应用场景涵盖任务调度、BFS、滑动窗口、进程调度、消息中间件等。与栈(LIFO)核心区别在于数据访问顺序。提供C/C++实现示例(含STL)。选择取决于容量预估、动态性、并发需求等因素,理解队列是掌握算法与并发编程的基础。

2402_85743155的博客 338
上一篇: 顺序队列的读写
下一篇: ffmpeg基本目录、文件、日志操作
agentky
博客等级 码龄7年 39粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值