首页 > 其他分享 >使用条件变量模拟消费者和生产者

使用条件变量模拟消费者和生产者

时间:2023-08-23 20:35:09浏览次数:26  
标签:变量 生产者 互斥 线程 mp pthread 缓冲区 模拟

题目简介

生产者和消费者问题是一个经典的多线程同步问题,涉及到一个共享的缓冲区,生产者将数据放入缓冲区,消费者从缓冲区中取出数据。问题的关键是要确保生产者和消费者之间的正确交互,避免生产者在缓冲区满时继续生产,消费者在缓冲区空时继续消费。

解决方案

使用条件变量是一种常见的解决方案,它可以用于线程之间的通信和同步。条件变量允许线程等待某个条件的发生,并在条件满足时被唤醒。在生产者和消费者问题中,可以使用两个条件变量,一个用于生产者等待缓冲区不满的条件,另一个用于消费者等待缓冲区不空的条件。

在这里我使用的是链表版本的存储空间,不需要考虑生产者等待缓冲区满的情况,所以只需要一个条件变量用于消费者等待缓冲区不空即可。

下面是使用条件变量实现生产者和消费者问题的一般步骤:

  1. 定义共享的缓冲区和相关的变量,如缓冲区大小、缓冲区数据、缓冲区中的数据数量等。
  2. 定义互斥锁,用于保护对缓冲区的访问,确保同时只有一个线程能够修改缓冲区。
  3. 定义条件变量,一个用于消费者等待缓冲区不空的条件。
  4. 生产者线程循环执行以下操作:
  • 加锁互斥锁,保证对缓冲区的互斥访问。
  • 将数据放入缓冲区。
  • 解锁互斥锁。
  • 唤醒等待缓冲区不空的条件变量的消费者线程。
  1. 消费者线程循环执行以下操作:
  • 加锁互斥锁,保证对缓冲区的互斥访问。
  • 检查缓冲区是否为空,如果为空,则等待缓冲区不空的条件变量。
  • 从缓冲区取出数据。
  • 解锁互斥锁。

通过使用条件变量,生产者和消费者可以在合适的时机等待和唤醒,以实现正确的同步和互斥操作,避免了忙等待的情况。

编码流程

  1. 定义了一个结构体msg作为链表节点,表示产品。结构体包含一个指向下一个节点的指针next和一个表示产品编号的整数num
  2. 声明了一个全局变量head,表示链表的头指针。
  3. 静态初始化了一个条件变量has_product和一个互斥量lock
  4. 定义了消费者线程函数consumer,在一个无限循环中执行消费者的操作。首先,使用互斥锁lock对临界区进行加锁。然后,使用一个while循环判断链表头指针head是否为空,如果为空,说明没有产品可供消费,此时调用pthread_cond_wait等待条件变量has_product的触发。在等待期间,消费者线程会释放互斥锁并等待被唤醒。当被唤醒后,再次检查链表头指针是否为空。如果不为空,说明有产品可供消费,将头节点取出并更新头指针。最后,释放互斥锁并打印消费的产品编号,然后释放节点的内存并休眠一段时间。
  5. 定义了生产者线程函数producer,在一个无限循环中执行生产者的操作。首先,动态分配一个新的节点mp作为产品,并为num赋予一个随机值。然后,打印生产的产品编号。接下来,使用互斥锁lock对临界区进行加锁。将新节点插入链表的头部,并更新头指针。最后,释放互斥锁,并调用pthread_cond_signal唤醒等待在条件变量has_product上的一个消费者线程。最后,休眠一段时间。
  6. main函数中,创建了一个生产者线程和一个消费者线程,并使用pthread_join等待线程的结束。
  7. 通过调整休眠时间,可以模拟生产者和消费者的不同速度和数量。

代码展示

//借助条件变量模拟生产者,消费者问题
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

//链表作为共享熟路,需要被互斥量保护
struct msg{
struct msg *next;
int num;
};

struct msg *head;

//静态初始化,一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *consumer(void *p){
struct msg *mp;
for(;;){
pthread_mutex_lock(&lock);
while(head == NULL){ //头指针为空,说明没有节点
pthread_cond_wait(&has_product, &lock);

	}
	mp = head;
	head = mp->next;	//模拟消费掉一个产片
	pthread_mutex_unlock(&lock);
	printf("Consume %lu---%d\n", pthread_self(), mp->num);
	free(mp);
	sleep(rand() % 5);
}
return NULL;
}

void *producer(void *p){
struct msg *mp;
for(;;){
mp = malloc(sizeof(struct msg));
mp->num = rand() % 1000 + 1; //模拟生产一个产品
printf("Produce =============%d\n", mp->num);
pthread_mutex_lock(&lock);
mp->next = head;
head = mp;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&has_product); //将等待在该条件变量的一个线程唤醒
sleep(rand() % 5);
}
return NULL;
}

int main(){
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}

运行结果

使用条件变量模拟消费者和生产者_链表

注意事项

需要注意的是,在使用条件变量时,需要先加锁互斥锁,再进行条件判断和等待操作,以确保线程在等待条件变量时不会出现竞态条件。同时,在修改条件变量相关的状态之后,需要唤醒等待该条件的线程,以便它们能够重新检查条件并继续执行。

标签:变量,生产者,互斥,线程,mp,pthread,缓冲区,模拟
From: https://blog.51cto.com/u_16158769/7206395

相关文章

  • m基于FPGA的高斯白噪声信道模拟系统verilog实现,包含testbench,可以配置不同的SNR和频
    1.算法仿真效果vivado2019.2仿真结果如下:SNR=0db,无频偏SNR=5db,无频偏SNR=25db,无频偏SNR=45db,带频偏2.算法涉及理论知识概要高斯白噪声信道在通信系统中具有重要意义,模拟此类信道有助于评估系统性能。本文提出的FPGA实现系统可以灵活地模拟不同信道条件,为通信系统的设计......
  • m基于FPGA的高斯白噪声信道模拟系统verilog实现,包含testbench,可以配置不同的SNR和频
    1.算法仿真效果vivado2019.2仿真结果如下:   SNR=0db,无频偏   SNR=5db,无频偏   SNR=25db,无频偏   SNR=45db,带频偏   2.算法涉及理论知识概要       高斯白噪声信道在通信系统中具有重要意义,模拟此类信道有助于评估系统性能。本......
  • Swift-基础语法之变量&常量&元组
    使用 let来声明一个常量,用 var来声明一个变量。常量的值在编译时并不要求已知,但是你必须为其赋值一次。这意味着你可以使用常量来给一个值命名,然后一次定义多次使用varmyVariable=42myVariable=50letmyConstant=42常量或者变量必须拥有和你赋给它们的值相同的类型。不......
  • Swift - 基本数据类型,及常/变量声明
    用oc编码已经有段时间了,这期间一直在使用oc和学习oc的编码技巧,忽闻小伙伴说:swift已经是趋势了,有时间多看看吧所以我也开始从网上搜索博客,感谢hangge.com的博客,如有冒犯之处,请多见谅,因为是看着大神的博客开始swif全面的学习之路!方向已定,无论前面有多难,都要走下去! 一、swift中基本的......
  • 8.22普及模拟三
    \(\large{T1\最大生成树}\\\tiny{30pts}\)题意:给定一个点权为\(a_i\)边权为\(|a_i-a_j|\)的完全图,求这个图的最大生成树部分分:30~50pts:Kruskal求最长路100pts:贪心,设权值最大点为\(a_{max}\),权值最小点为\(a_{min}\)则\(max(|a_{max}-a_i|,|a_{min}-a_i|)\)一定......
  • Windows修改环境变量的两种方式
    Windows环境永久修改环境变量命令行方式简单使用变量名不区分大小写1.设置用户变量setx"KEY""VALUE"2.设置全局变量setx"KEY""VALUE"/m3.追加Pathsetx"PATH""%PATH%;D:\ProgramFiles\"4.删除变量变量置空值,实际上变量仍然保存在注册表里setx"......
  • 2023.8.22 模拟赛
    ABFSB一个长\(n(n\le1e5)\)的字符串\(S\),长\(m(m\le30)\)的字符串\(T\),\(S\)的每个位置有权值\(a_i\)。\(q(q\le1e5)\)次询问\(l,r\),求\(T\)作为一个子序列出现在\(S(l,r)\)中的所有方案中,\(T\)出现的位置的权值和。先考虑\(a_i=1\)。显然有\(f_{i,j}=......
  • 模拟Linux文件管理员系统-shell实现
    目录模拟Linux文件管理员系统-shell实现1系统要求2脚本执行效果2.1管理员登录效果2.2普通用户登录效果2.3密码文件格式3实现脚本4密码文件5说明模拟Linux文件管理员系统-shell实现注:此脚本仅供学习使用,具体需要根据实际情况进行测试调整。1系统要求2脚本执行效果2......
  • 模拟集成电路设计系列博客——1.1.7 带有输出阻抗增强的宽摆幅电流镜
    1.1.7带有输出阻抗增强的宽摆幅电流镜下图的结构在[Gatti,1990],[Coban,1994;Martin,1994]中被提出和使用,与[Säckinger,1990]的输出阻抗电流镜结构很像,除了一个二极管接法的晶体管被加在共源级增强放大器前作为电压转换器。在输出光,电平转换器是通过\(I_{bias}\)偏置的二......
  • list类的模拟实现
    一、list的介绍list文档介绍1、list是序列容器,允许在序列内的任何位置执行恒定时间的插入和删除操作,以及双向迭代。2、list底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个节点和后一个节点。3、list与forward_list非常相似:最主要的......