线程同步之 生产者消费者模型详解

简介: 前言 博主本来没打算讲这个比较前面的知识的(博主socket编程还有两个部分没讲,进程也才写完回收僵尸进程的三种方法,信号捕捉器也才完结),但是今天有朋友来问博主,什么是生产者消费者模型,所以博主就先为为数不多的朋友把生产者消费者模型讲一讲,希望大家能看懂(没有现成和锁知识的朋友不要急,这部分是写给有基础的朋友看的,这些知识博主都会慢慢的讲到)。前言 博主本来没打算讲这个比较前面的知识的(博主socket编程还有两个部分没讲,进程也才写完回收僵尸进程的三种方法,信号捕捉器也才完结),但是今天有朋友来问博主,什么是生产者消费者模型,所以博主就先为为数不多的朋友把生产

前言

      博主本来没打算讲这个比较前面的知识的(博主socket编程还有两个部分没讲,进程也才写完回收僵尸进程的三种方法,信号捕捉器也才完结),但是今天有朋友来问博主,什么是生产者消费者模型,所以博主就先为为数不多的朋友把生产者消费者模型讲一讲,希望大家能看懂(没有现成和锁知识的朋友不要急,这部分是写给有基础的朋友看的,这些知识博主都会慢慢的讲到)。


什么是模型?

模型就是要解决某个问题的固定方法或套路,所以有时候我们学会一点模型是很有必要的。

生产者消费者条件变量模型

线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。

      换一句话来说就是,比如说你在学校上课,好不容易熬到了放学,此时你肯定会和你的小伙伴一起去食堂吃饭(土豪除外,土豪不去食堂),但是因为之前食堂停水停电,导致,你们放学了才开始做饭,所以现在就会面临一个问题。

      当食堂大叔做好饭之后会把饭放在指定的饭盒里面,但是食堂大叔做饭的速度可能比不上你和你小伙伴吃饭的速度(或者大叔做饭的速度太快导致们根本吃不过大叔的做饭速度)。这也就是我们的生产者消费者模型了。

所以博主先列出一些概念,更容易使大家理解:


生产者:产生数据的一方(食堂大叔做饭)

消费者:处理数据的一方(你和你的小伙伴吃饭)


我们会遇到的问题

双方处理数据的速度可能不同,这样就会导致总由一方吃处于阻塞状态。(也就是博主上面所说的吃饭的问题)可能博主的逻辑不是很严谨,但是将就着看吧。


要用到的函数解析:

int pthread_cond_wait


 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
函数作用:
1.阻塞等待条件变量cond(参1)满足  
2.释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
 1.2.两步为一个原子操作。
3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);
我们要知道的是在调用这个函数之前我们要做前准备好的事情:
1.mutex_t mutex 定义互斥量
2.init 初始化
3.lock 加锁
4.cond_t has_product 定义条件变量
5.inti  初始化


pthread_cond_signal


int pthread_cond_signal(pthread_cond_t *cond);
唤醒至少一个阻塞在条件变量上的线程


提到了这么多的东西了,接下来博主就给出生产者消费者模型的代码(里面有很详细的解释了,大家应该都能看懂,博主就不在做GIF动画了)


/*借助条件变量模拟 生产者-消费者 问题*/
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
/*链表作为公享数据,需被互斥量保护*/
struct msg {
    struct msg *next;
    int num;
};
struct msg *head;
struct msg *mp;
/* 静态初始化 一个条件变量 和 一个互斥量  完成 1 2   4 5 */
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *consumer(void *p)
{
    while(true){
        pthread_mutex_lock(&lock);  //加锁  完成 3 
        while (head == NULL) {           //头指针为空,说明没有节点    可以为if吗
            pthread_cond_wait(&has_product, &lock);   //此时会阻塞等待 等生产者传入之后才会继续工作
        /*  1.阻塞等待条件变量cond(参1)满足  
          2.释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
            注意:   1和2这两步为一个原子操作。
          3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);*/
        }
    mp = head;      
        head = mp->next;    //模拟消费掉一个产品
        pthread_mutex_unlock(&lock);  //解锁
        printf("-Consume ---%d\n", mp->num);
        free(mp); //create 和 free 要成双成对出现
        mp = NULL;  
        sleep(rand() % 5);  //随机随眠让 CUP 转移
    }
}
void *producer(void *p)
{
   while(true) {
        mp = malloc(sizeof(struct msg));
        mp->num = rand() % 1000 + 1;        //模拟生产一个产品
        printf("-Produce ---%d\n", mp->num);
        pthread_mutex_lock(&lock);  //加锁(不用担心死锁,即使是消费者先调用,在pthread_cond_wait()之后也会将之前获得的锁给释放!
      //头插法
    mp->next = head;
        head = mp;
        pthread_mutex_unlock(&lock);  //解锁
        pthread_cond_signal(&has_product);  //将等待在该条件变量上的一个线程(生产者线程)唤醒
        sleep(rand() % 5);  //随机随眠让 CUP 转移
    }
}
int main(int argc, char *argv[])
{
    pthread_t pid, cid; //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;
}

有朋友会遇到这种问题:

我为什么写的不能实现同步呢?就只是一个生产者在那干活

博主认为最有可能的原因是因为时序竞态,这种解决办法很简单加sleep就行了。

或者是 博主上面的两个函数没使用好导致这种情况,要记住 wait等的是生产的信号,signal发的是生产者的信号去唤醒阻塞的消费者。


目录
相关文章
|
12天前
|
存储 安全 Java
Qt线程池+生产者消费者模型
Qt线程池+生产者消费者模型
39 5
|
7天前
|
消息中间件 监控 Java
滴滴面试:谈谈你对Netty线程模型的理解?
Netty 线程模型是指 Netty 框架为了提供高性能、高并发的网络通信,而设计的管理和利用线程的策略和机制。 **Netty 线程模型被称为 Reactor(响应式)模型/模式,它是基于 NIO 多路复用模型的一种升级,它的核心思想是将 IO 事件和业务处理进行分离,使用一个或多个线程来执行任务的一种机制。** ## 1.**Reactor三大组件** Reactor 包含以下三大组件: ![image.png](https://cdn.nlark.com/yuque/0/2024/png/92791/1717079218890-89000a00-48bc-4a1a-b87e-e1b6
21 2
|
8天前
|
安全 调度
鸿蒙HarmonyOS实战-Stage模型(线程模型)
本文介绍了线程作为操作系统调度的最小单位,是进程中的执行流,具有轻量级、并发执行、共享资源、通信简单和上下文切换等特点。线程分为用户线程和内核线程,其中内核线程由操作系统管理,而用户线程由应用程序控制。线程用于提高程序性能和响应速度,尤其适合多任务并发处理。在HarmonyOS中,每个应用有主线程负责UI和 ArkTS 引擎管理,以及Worker线程执行耗时任务。线程间通信可通过Emitter实现事件同步和Worker进行异步操作。
|
10天前
|
设计模式 安全 Java
多线程(代码案例: 单例模式, 阻塞队列, 生产者消费者模型,定时器)
多线程(代码案例: 单例模式, 阻塞队列, 生产者消费者模型,定时器)
22 2
|
13天前
|
NoSQL 网络协议 关系型数据库
redis-学习笔记(redis 单线程模型)
redis-学习笔记(redis 单线程模型)
25 3
|
13天前
|
Linux 调度 Windows
【操作系统】线程、多线程模型
【操作系统】线程、多线程模型
27 1
|
1天前
|
缓存 Java 测试技术
Java性能优化(八)-多线程调优-线程池大小设置
Java性能优化(八)-多线程调优-线程池大小设置
4 0
|
3天前
|
安全 Java 容器
多线程(进阶四:线程安全的集合类)
多线程(进阶四:线程安全的集合类)
11 0
|
3天前
|
开发框架 监控 Java
【.NET Core】多线程之线程池(ThreadPool)详解(二)
【.NET Core】多线程之线程池(ThreadPool)详解(二)
24 3
|
3天前
|
SQL 开发框架 Java
【.NET Core】多线程之线程池(ThreadPool)详解(一)
【.NET Core】多线程之线程池(ThreadPool)详解(一)
20 2