精华内容
下载资源
问答
  • 什么是生产者消费者模型 在 工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产 生数据的模块,就形象地称为...
  • 主要介绍了Java基于Lock的生产者消费者模型,结合实例形式分析了java基于锁机制的生产者消费者模型相关实现与使用技巧,需要的朋友可以参考下
  • 主要介绍了Java多线程 BlockingQueue实现生产者消费者模型详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 生产者消费者模型 在平时的编程中 经常遇到一个线程要产生数据 而另一个线程要处理产生 出来的数据 这其实就是生产者和消费者的关系 生产者在产生数据后可以直接 调用消费者处理数据 也可以把数据放在一个缓冲区中...
  • 主要介绍了python之生产者消费者模型实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 多线程实现生产者消费者模型:锁(Lock)、信号量(Semaphore、BoundedSemaphore)、条件(Condition)、队列(Queue)、事件(Event) 多进程程实现生产者消费者模型:信号量(Semaphore)、条件(Condition)、...
  • 生产者消费者模型

    千次阅读 多人点赞 2017-02-20 23:33:26
    一、什么是生产者消费者模型 在实际的开发中,经常会碰到如下场景:某个模块负责生产数据,这些数据由另一个模块来负责处理。产生数据的模块就形象的称为生产者,而处理数据的模块就称为消费者。只有生产者和消费...

    一、什么是生产者消费者模型
    在实际的开发中,经常会碰到如下场景:某个模块负责生产数据,这些数据由另一个模块来负责处理。产生数据的模块就形象的称为生产者,而处理数据的模块就称为消费者。只有生产者和消费者还不够,这个模型还必须要有一个缓冲区处于生产者和消费者之间,作为中介。生产者把数据放入缓冲区,而消费者从缓冲区中取出数据。
    这里写图片描述

    二、为什么要使用生产者消费者模型
    1、解耦
    假设生产者和消费者是两个类,如果让生产者直接调用消费者的某个函数,那么生产者和消费者之间就会产生依赖(耦合)。如果消费者的代码发生变化可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合性也就降低了。

    2、支持并发
    生产者直接调用消费者的某个方法,还有一个弊端。由于函数调用是同步的(或者称作阻塞的),在消费者的方法没有返回之前,生产者只好一值阻塞,如果消费者处理数据很慢,则生产者就会白白浪费时间。使用生产者和消费者模式之后,生产者和消费者可以是两个独立的并发主体。生产者把制造出来的数据放到缓冲区,就可以继续生产下一个数据,而不必依赖消费者的处理速度了。
    3、支持忙闲不均
    缓冲区还有一个好处就是,如果制造数据的速度时快时慢,缓冲区的好处就体现出来了,当数据制造快的时候,消费者来不及处理,未处理的数据就可存放到缓冲区中。等待生产者的速度慢下来之后,消费者再慢慢处理。

    三、生产者消费的关系和特点
    1、生产者和消费者的模型
    1.1、单个生产者和单个消费者
    1.2、单个生产者和多个消费者
    1.3、多个生产者和单个消费者
    1.4、多个生产者和多个消费者

    2、生产者和消费者之间的关系
    2.1、生产者和生产者之间是互斥关系
    2.2、消费者和消费者之间是互斥关系
    2.3、生产者和消费者之间是同步、互斥关系
    也就是:
    生产者生产的时候消费者不能消费
    消费者消费的时候生产者不能生产
    缓冲区空时消费者不能消费
    缓冲区满时生产者不能生产

    例: 生产者-消费者的例子,生产者生产一个结构体串在链表的表头上,消费者从表头取一个结构体。

    #include<stdio.h>
    #include<pthread.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<assert.h>
    
    typedef struct Node
    {
        int data;
        struct Node * next;
    }Node,*Node_p,**Node_pp;
    
    Node_p CreatNode(int data)
    {
        Node_p _n=(Node_p)malloc(sizeof(Node));
        if(_n==NULL)
        {
            return NULL;
        }
        _n->data=data;
        _n->next=NULL;
        return _n;
    }
    
    void Init(Node_pp list)
    {
        *list=CreatNode(0);
    }
    
    void PushFront(Node_p list ,int data)
    {
        assert(list);
        Node_p _n=CreatNode(data);
        if(_n==NULL)
        {
            perror("Push");
            return;
        }
    
        _n->next=list->next;
        list->next=_n;
    }
    
    void del_Node(Node_p del)
    {
        assert(del);
        free(del);
    }
    
    void PopFront(Node_p list,int *data)
    {
        if(!isEmpty(list))
        {
            Node_p del=list->next;
            list->next=del->next;
            *data=del->data;
            del_Node(del);
        }
        else
        {
            printf("list Empty\n");
        }
    }
    
    int isEmpty(Node_p list)
    {
        assert(list);
        if(list->next==NULL)
            return 1;
        else
            return 0;
    }
    
    void destroy(Node_p list)
    {
        int data;
        assert(list);
        while(!isEmpty(list))
        {
            PopFront(list,&data);
        }
        del_Node(list);
    }
    
    void ShowList(Node_p list)
    {
        assert(list);
        Node_p cur=list->next;
        while(cur->next)
        {
            printf("%d->",cur->data);
            cur=cur->next;
        }
        printf("\n");
    }
    
    Node_p list=NULL;
    pthread_mutex_t mylock= PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t mycond=PTHREAD_COND_INITIALIZER;
    
    void * Consumer(void *arg)
    {
        int data=0;
        while(1)
        {
            pthread_mutex_lock(&mylock);
            while(isEmpty(list))
            {
                pthread_cond_wait(&mycond,&mylock);
            }
            PopFront(list,&data);
            pthread_mutex_unlock(&mylock);
            printf("consumer:%d\n",data);
        }
        return NULL;
    }
    
    void * Producer(void *arg)
    {
        int data=0;
        while(1)
        {
            usleep(123456);
            data=rand()%1000;
            pthread_mutex_lock(&mylock);
            PushFront(list,data);
            pthread_mutex_unlock(&mylock);
            pthread_cond_signal(&mycond);
            printf("Producer:%d\n",data);
        }
        return NULL;
    }
    
    int main()
    {
        Init(&list);
        pthread_t tid1,tid2;
        pthread_create(&tid1,NULL,Consumer,NULL);
        pthread_create(&tid2,NULL,Producer,NULL);
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        destroy(list);
        pthread_mutex_destroy(&mylock);
        pthread_cond_destroy(&mycond);
        return 0;
    }
    展开全文
  • wpf窗体多线程实现生产者消费者模型(利用信号量),工作线程利用回调函数改变窗体线程的控件。
  • Qt入门练习项目——生产者消费者模型 Qt入门练习项目源码,通过本项目从而了解Qt,进行一个简单的入门。 本文件是对应项目源码,希望多多交流。
  • 主要介绍了Java多线程 生产者消费者模型实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 文章目录生产者消费者模型什么是生产者消费者模型生产者消费者模型的321原则生产者消费者模型的优点生产者消费者模型的实现方法基于循环队列,信号量实现基于阻塞队列,互斥锁、条件变量实现 生产者消费者模型 什么...


    生产者消费者模型

    什么是生产者消费者模型

    生产者消费者模型是针对在任务处理中既要产生数据,又要处理数据这一情景而设计出来的一种解决方案。如果生产者生产资源很快,消费者处理资源的速度很慢,则生产者就必须等待消费者处理完数据才能继续生产,反之同理,这样的话生产者与消费者之间的耦合度较高(依赖关系),导致总体效率较低。
    于是通过引入一个交易场所(缓冲区),来解决生产者与消费者之间的强耦合关系生产者与消费者之间不是直接通讯,而是通过这个交易场所来间接通讯,将两者的直接关系转变成间接关系,生产者生产完数据直接交给仓库,而消费者要使用则直接从缓冲区取出数据,这样效率就大大的提高。
    在这里插入图片描述

    生产者消费者模型的321原则

    1. 一个场所
      交易场所:缓冲区

    2. 两个角色
      生产者:生产资源的角色,往交易场所放数据
      消费者:消费资源的角色,从交易场所取数据

    3. 三种关系
      生产者与消费者之间的同步与互斥关系
      生产者与生产者之间的互斥关系
      消费者与消费者之间的互斥关系

    生产者消费者模型的优点

    • 解耦合:
      生产者与消费者之间不直接通讯,而是借助交易场所通讯,降低了耦合度
    • 支持忙闲不均:
      可以解决某一方过忙而某一方过闲的问题
    • 支持并发:
      多线程轮询处理

    生产者消费者模型的实现方法

    基于循环队列,信号量实现

    可以借助循环队列来作为交易场所,用信号量实现同步与互斥关系

    #include<iostream>
    #include<pthread.h>
    #include<vector>
    #include<semaphore.h>
    using namespace std;
    
    const size_t MAX_SIZE = 5;
    
    class CircularQueue
    {
        public:
            CircularQueue(int capacity = MAX_SIZE) 
                : _queue(capacity) 
                , _capacity(capacity)
                , _front(0)
                , _rear(0)
            {
                sem_init(&_mutex, 0, 1);
                sem_init(&_full, 0, 0);
                sem_init(&_empty, 0, _capacity);
            }
            
            ~CircularQueue()
            {
                sem_destroy(&_mutex);
                sem_destroy(&_full);
                sem_destroy(&_empty);
            }
            
            //队尾入队
            void Push(int data)
            {
                //入队前先减少一个空闲资源,防止其他进程争抢资源,如果资源不够则阻塞
                sem_wait(&_empty);
                sem_wait(&_mutex);
                
                _queue[_rear++] = data;
                _rear %= _capacity;
               
                sem_post(&_mutex);
                sem_post(&_full);
                //入队完成,使用资源+1,同时唤醒消费者使用资源
            }
    
            //队头出队
            void Pop(int& data)
            {
                //出队前减少当前使用的资源数
                sem_wait(&_full);
                sem_wait(&_mutex);
                
                data = _queue[_front++];
                _front %= _capacity;
               
                sem_post(&_mutex);
                sem_post(&_empty);
                //出队完成,唤醒生产者生产资源
            }
    
        private:
            vector<int> _queue;
            size_t _capacity;
            size_t _front;
            size_t _rear;
    
            //需要三个信号量,一个互斥信号量,一个信号量表示空闲资源,一个信号量正在使用资源
            sem_t _mutex;
            sem_t _full;
            sem_t _empty;
            
    };
    
    void* productor(void* arg)
    {
        //因为入口函数的参数为void*,所以需要强转回对应类型
        CircularQueue* queue = (CircularQueue*)arg;
        int data = 0;
    
        while(1)
        {
            queue->Push(data);
            cout << "生产者存入数据:" << data++ << endl;
        }
    
        return NULL;
    }
    
    void* customer(void* arg)
    {
        CircularQueue* queue = (CircularQueue*)arg;
        int data;
    
        while(1)
        {
            queue->Pop(data);
            cout << "消费者取出数据:" << data << endl;
        }
    
        return NULL;
    }
    
    int main()
    {
        CircularQueue queue;
        pthread_t pro_pid[5], cus_pid[5];
        
        //创建线程
        for(int i = 0; i < 5; i++)
        {
            int ret = pthread_create(&pro_pid[i], NULL, productor, (void*)&queue);
            if(ret)
            {
                cout << "生产者进程创建失败" << endl;
                return -1;
            }
            
            ret = pthread_create(&cus_pid[i], NULL, customer, (void*)&queue);
    
            if(ret)
            {
                cout << "消费者进程创建失败" << endl;
                return -1;
            }
        }
    
        //线程等待
        for(int i = 0; i < 5; i++)
        {
            pthread_join(pro_pid[i], NULL);
            pthread_join(cus_pid[i], NULL);
        }
    
        return 0;
    }
    

    这种方法是教科书上常用的,实现起来也较为简单,但是有一个需要注意的问题,
    出队和入队时,互斥信号量一定要放在条件信号量后面,否则会引起死锁问题
    这里以生产者举例子,假设如果先执行wait(mutex),此时如果没有空闲的空间,生产者就会阻塞,此时到消费者进程,消费者却因为mutex已经加锁,无法进行后面的post操作使互斥信号量mutex进行解锁,此时消费者也会被阻塞,这时两者都期待对方解锁,但是双方都阻塞,无法解锁,导致程序进入了永眠状态,也就是死锁。

    运行效果
    在这里插入图片描述

    基于阻塞队列,互斥锁、条件变量实现

    借助阻塞队列作为交易场所,互斥锁实现互斥关系,条件变量实现同步关系

    #include<iostream>
    #include<unistd.h>
    #include<pthread.h>
    #include<queue>
    
    using namespace std;
    
    const size_t MAX_SIZE = 5;
    
    class BlockQueue
    {
        public:
            BlockQueue(size_t capacity = MAX_SIZE) : _capacity(capacity)
            {
                pthread_mutex_init(&_mutex, NULL);
                pthread_cond_init(&_pro_cond, NULL);
                pthread_cond_init(&_cus_cond, NULL);
            }
    
            ~BlockQueue()
            {
                pthread_mutex_destroy(&_mutex);
                pthread_cond_destroy(&_pro_cond);
                pthread_cond_destroy(&_cus_cond);
            }
    
            void Push(int data)
            {
                //加锁保证线程安全
                pthread_mutex_lock(&_mutex);
    
                //队列已满,阻塞生产者生产
                //循环等待,防止有其他进程进入
                while(_queue.size() == _capacity)
                {
                    pthread_cond_wait(&_pro_cond, &_mutex);
                }
    
                //数据入队
                _queue.push(data);
    
                pthread_mutex_unlock(&_mutex);
                //生产完毕,唤醒消费者消费
                pthread_cond_signal(&_cus_cond);
    
            }
    
            void Pop(int& data)
            {
                pthread_mutex_lock(&_mutex);
    
                while(_queue.empty())
                {
                    //队列空,阻塞消费者消费
                    pthread_cond_wait(&_cus_cond, &_mutex);
                }
    
                //数据出队
                data = _queue.front();
                _queue.pop();
                
                pthread_mutex_unlock(&_mutex);
    
                //消费完毕,唤醒生产者生产
                pthread_cond_signal(&_pro_cond);
            }
    
        private:
            queue<int> _queue;
            size_t _capacity;
    		
    		//实现互斥关系的互斥锁
            pthread_mutex_t _mutex;
            //实现同步关系的条件变量
            pthread_cond_t _pro_cond;
            pthread_cond_t _cus_cond;
    };
    
    //因为入口函数的参数必须为void* ,所以要强转成BlockQueue类型
    void *producter(void *arg)
    {
        BlockQueue *queue = (BlockQueue*)arg;
        //生产者不断生产数据
        
        int data = 0;
        while(1)
        {
            //生产数据
            queue->Push(data);
    
            cout << "生产者放入数据:" << data++ << endl;
        }
    
        return NULL;
    }
    
    void *customer(void *arg)
    {
        BlockQueue *queue = (BlockQueue*)arg;
        //消费者不断取出数据
        while(1)
        {
            //取出数据
            int data;
            queue->Pop(data);
    
            cout << "消费者取出数据:" << data << endl;
        }
    
        return NULL;
    }
    
    int main()
    {
        BlockQueue queue;
        pthread_t pro_tid[5], cus_tid[5];
    	
        //创建线程
        for(size_t i = 0; i < 5; i++)
        {
            int ret = pthread_create(&pro_tid[i], NULL, producter, (void*)&queue);
    
            if(ret)
            {
                cout << "生产者线程创建失败" << endl;
                return -1;
            }
    
            ret = pthread_create(&cus_tid[i], NULL, customer, (void*)&queue);
            if(ret)
            {
                cout << "消费者线程创建失败" << endl;
                return -1;
            }
        }   
        
        //等待线程退出
        for(size_t i = 0; i < 4; i++)
        {
            pthread_join(pro_tid[i], NULL);
            pthread_join(cus_tid[i], NULL);
        }
    
        return 0;
    }
    
    

    在这里我们又会看到,这里是先对互斥锁加锁,再对条件变量进行wait操作,这里为什么不会引起死锁呢?
    因为条件变量的pthread_cond_wait操作其实集合了三个操作,他会首先对互斥锁解锁,然后判断条件变量是否达成,没有则阻塞,之后再对互斥锁进行加锁,所以这样就不会因为没人解锁而陷入死锁状态。

    实现效果
    在这里插入图片描述

    展开全文
  • 生产者消费者,线程同步,模型,锁
  • java多线程基础生产者消费者模型,自己用xmind做的思维导图,帮助小白快速熟悉多线程的操作。
  • 主要介绍了Java实现简易生产者消费者模型过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 下面小编就为大家带来一篇Queue 实现生产者消费者模型(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 操作系统------生产者消费者模型

    千次阅读 2018-04-24 18:06:59
    在“进程间通信----信号量”一文中,有简单介绍过生产者消费者模型的基本概念。在下文中将使用有关线程的互斥与同步的相关概念来实现两种不同类型的生产者消费者模型。在本文中侧重于线程间同步的实现。有关线程互斥...

            在“进程间通信----信号量”一文中,有简单介绍过生产者消费者模型的基本概念。在下文中将使用有关线程的互斥与同步的相关概念来实现两种不同类型的生产者消费者模型。在本文中侧重于线程间同步的实现。有关线程互斥与同步的相关概念见线程的互斥与同步一文:

            首先介绍该模型的背景知识:

    生产者消费者问题

            该问题是一个著名的同步问题。它描述的是:一群生产者进程正在生产产品,并将这些产品提供给消费者进程去消费。为使生产者和消费者能够并发执行。在两者之间设置了一个公共区域,生产者进入公共区域生产产品并放入其中。消费者进入公共区域并取走产品进行消费。

            当一个生产者进入公共区域生产产品时,其他生产者和消费者不能同时进入公共区域生产产品或消费产品。当一个消费者进入公共区域消费产品的时候,其它消费者和生产者不能同时进入该区域消费产品或生产产品。也就是说,任意时刻,最多只允许一个生产者或一个消费者进入公共区域。即生产者和消费者必须互斥的访问公共区域。

            当产品放满公共区域时,生产者必须等待,使消费者先消费。当公共区域为空时,消费者必须等待,使生产者先生产。即在公共区域为空或为满时,生产者或消费者在执行时要满足一定的先后顺序。即生产者与消费者对公共区域的访问必须同步。

            根据以上描述,可以得出如下结论:

    (1)生产者与生产者之间存在竞争即互斥关系

    (2)消费者与消费者之间存在竞争即互斥关系

    (3)生产者与消费者之间存在互斥与同步关系

            以上三个结论可以概括为:三种关系,两种角色,一个临界区。即“三二一”规则。

            下面根据具体研究不同类型的生产者消费者模型:

    利用链表做临界资源的生产者消费者模型:

            在该模型中临界资源为一带头节点的单向链表。多个生产者在向链表中头插节点,多个消费者在对链表进行头删结点。

            在一个生产者头插结点或一个消费者在头删结点的过程中,其他生产者或消费者不能对该链表进行操作。因此:生产者与生产者之间,消费者与消费者之间,生产者与消费者之间存在互斥关系。在这里,通过互斥量来保证互斥关系。

            因为链表是动态插入结点,所以可以说链表没有满的时候(不考虑内存满的情况)。所以这里只需考虑链表为空的时候。当链表为空时,生产者必须先执行头插结点。此时,便需要生产者与消费者在执行时满足一定的顺序要求。即生产者与消费者必须同步的访问链表。

            通过以上分析,生产者与消费者之间满足上述的三个结论。

    1. 利用条件变量保证线程间的同步

            在该模型中,利用多个线程来模拟生产者与消费者,互斥量来保证生产者消费者任意两者之间的互斥关系,条件变量来保证生产者与消费者之间的同步关系。当链表为空时,使用条件变量使消费者线程挂起等待。有关互斥量与条件变量的使用见本文开头的博客链接。

            下面模拟该模型:

    定义互斥量和条件变量:

    pthread_mutex_t mutex;                                                                                                                
    pthread_cond_t cond; 

    消费者消费产品

    //消费者进行头删节点
    void* consume(void* arg)
    {
        while(1)//消费者一直进行头删结点
        {
            pthread_mutex_lock(&mutex);//在头删结点时,先进入临界区获得临界资源,此时其他生产者或消费者不能在进入临界区,所以用互斥量
            while(head->next == NULL)
            //若while为if,可能会被异常唤醒。唤醒后,可能还是没有数据,就会对空链表进行操作,从而非法访问内存,所以即使被异常唤醒还要判断是否为空
            {
                printf("空链表\n");
                pthread_cond_wait(&cond,&mutex);//当链表为空的时候,消费者必须挂起等待使生产者先运行,此时用条件变量来实现二者之间的同步
            }
            printf("%d consume %d\n",(int)arg,head->next->data);//链表不为空时,消费者头删结点
            LinkListPopFront();
            pthread_mutex_unlock(&mutex);//当该消费者消费结束时,退出临界区,解锁互斥量
            usleep(5000000);//一个消费者0.5s消费一个
        }
    }

    生产者生产产品:

    //生产者进行头插节点
    void* product(void* arg)
    {
        while(1)//生产者一直头插结点
        {
            //生产者在对链表进行操作时,必须先获取维护临界资源的互斥量。申请成功之后,才能对链表进行操作,同时其他人不能进入该临界区
            pthread_mutex_lock(&mutex);
            LinkListPushFront();//生产者头插结点
            printf("%d product %d\n",(int)arg,head->next->data);
            pthread_cond_signal(&cond);//在插入节点后唤醒等待挂起中的消费者,如果没有挂起的消费者,则该语句不执行任何操作                                                                                                   
            pthread_mutex_unlock(&mutex);//解锁互斥量,退出临界区
            usleep(1000000);//一个生产者1s中生产一个
        }
    }

    主函数创建生产者消费者线程:

    int main()
    {
        LinkListInit();
        pthread_mutex_init(&mutex,NULL);//初始化互斥量
        pthread_cond_init(&cond,NULL);//初始化条件变量
        srand((unsigned int)time(NULL));
        pthread_t tid[8];
        int i = 0;
        for(;i < 3;++i)
        {   
            pthread_create(&tid[i],NULL,product,(void*)i);//创建3个生产者线程
        }   
        for(i = 3;i < 8;++i)
        {   
            pthread_create(&tid[i],NULL,consume,(void*)i);//创建5个生产者线程
        }   
        for(i = 0;i < 8;i++)
        {   
            pthread_join(tid[i],NULL);//线程等待
        }   
        pthread_mutex_destroy(&mutex);//销毁互斥量
        pthread_cond_destroy(&cond);//销毁条件变量
        return 0;
    }              

            上述有关头插结点和头删结点的操作见“单链表的基本操作”一文。

            当去掉上述两个代码段中的红色字体标注的条件变量的相关操作时,出现如下结果:

    空链表
    空链表
    空链表
    空链表
    空链表^C
    

            出现上述结果的原因是,在上述代码中,消费者的速度比生产者快。当消费者将结点都删除完时,由于消费者比生产者优先级高,每次竞争互斥量时,都是消费者竞争到,但消费者此时什么也做不了。即占着锁而做着无用的事情,但消费者想做有用的事情却申请不到互斥量,从而造成了消费者的死锁问题,使二者之间不能高效运行。

            加上上述两个代码段中的红色字体标注的条件变量的相关操作时,出现如下结果:

    空链表
    空链表
    空链表
    2 product 32
    1 product 35
    5 consume 35
    3 consume 32
    0 product 100
    7 consume 100
    空链表
    空链表
    空链表
    ^C
    

            上述结果可以看到,当链表为空时,消费者并没有一直占着锁而做无谓的等待,而是使生产者先生产完产品之后,在去消费。这样二者之间便能高效的合作。

            因为,条件变量变保证了线程之间的同步问题。

    2. 利用信号量保证线程间的同步

            对于上述的链表做临界资源的生产者消费者模型,还可以使用信号量来保证线程间的同步问题。在本例中,可以设置一个信号量size来表示链表中结点的个数,初始设置为0。当生产者头插一个结点,size加1,消费者头删一个结点,size减1。

            当信号量size的值为0时,消费者挂起等待。确保生产者先运行。这样,便可以保证生产者与消费者之间的同步问题。

            有关信号量保证线程间同步的具体实现方法参考见下一个模型。

    利用循环队列做临界资源的生产者消费者模型

        

            在该模型中临界资源为一循环队列。多个生产者从队尾向队列中插入节点,多个消费者从队首删除队列中的结点。

            在一个生产者插入结点或一个消费者在删除结点的过程中,其他生产者或消费者不能对该队列进行操作。因此:生产者与生产者之间,消费者与消费者之间,生产者与消费者之间存在互斥关系。在这里,通过互斥量来保证互斥关系。

            该循环队列是由数组模拟实现的。所以队列的长度是有限的。当队列为空时,生产者必须先执行向队列中插入节点。当队列为满时,消费者必须先执行删除节点。在这两种情况下,便需要生产者与消费者在执行时满足一定的顺序要求。即生产者与消费者必须同步的访问链表。

    1. 利用信号量来保证线程间的同步

            与上例类似,用多个线程来分别模拟生产者和消费者。互斥量来保证任意二者之间的互斥关系。利用信号量来保证生产者与消费者之间的同步关系。

            设置两个信号量full和empty。full表示队列中结点的个数。empty表示队列中没有节点的数组元素个数。比如说,一个长度为10的数组,现在往数组中插入个3个元素,此时,full为3,empty即为7。

            当信号量full的值为0即此时队列为空时,生产者线程必须先运行。当empty的值为0即此时队列为满时,消费者必须先运行。

            有关信号量的使用接口见线程的同步与互斥一文。

            下面利用信号量来演示该模型(这里假设队列中的数组最大长度为20):

    设置互斥量和信号量:

    pthread_mutex_t mutex;                                                                                                                
    sem_t full;
    sem_t empty;

    生产者向队列中插入节点:

    //生产者生产产品
    void* product(void* arg)
    {
        while(1)
        {   
            sem_wait(&empty);
            //首先:empty减1,即empty维护的临界资源个数减1
            //然后:如果empty值小于0,则将该线程挂起等待,使消费者先运行。否则什么也不做
            pthread_mutex_lock(&mutex);//生产者插入节点时要互斥的访问临界资源
            QueuePush();//插入节点
            int pre = 0;
            if(queue.tail == 0)
            {
                pre = 19;
            }
            else
            {
                pre = queue.tail - 1;
            }
            printf("%d product %d\n",(int)arg,queue.data[pre]);
            pthread_mutex_unlock(&mutex);//对临界资源操作完毕后,解锁互斥量,退出临界区
            sem_post(&full);
            //full加1,即full维护的临界资源个数加1,即元素个数加1
            //如果有消费者在挂起等待,则唤醒他。如果没有,则什么也不做                                                                    
            usleep(1);
        }
    }

    消费者消费产品:

    //消费者消费产品
    void* consume(void* arg)
    {
        while(1)
        {
            sem_wait(&full);
            //首先:full减1,即full维护的临界资源个数减1
            //然后:如果full值小于1,则将该线程挂起等待,使生产者先运行。否则什么也不做
            pthread_mutex_lock(&mutex);//消费者删除节点时要互斥的访问临界资源
            printf("%d consume %d\n",(int)arg,queue.data[queue.head]);
            QueuePop();//删除节点
            pthread_mutex_unlock(&mutex);//对临界资源操作结束,解锁互斥量,退出临界区
            sem_post(&empty);
            //首先:empty加1,即empty维护的临界资源个数加1000000
            //然后:如果有生产者在挂起等待,则唤醒他。如果没有,则什么也不做
            usleep(1000000);
        }
    }

            注意:在上述两段代码中申请互斥量和申请信号量的顺序不能交换。如果先申请互斥量,进入临界区后发现没有临界资源就会在临界区中挂起等待造成死锁问题。

    主线程中创建生产者消费者线程:

    int main()
    {
        srand((unsigned int)time(NULL));
        pthread_mutex_init(&mutex,NULL);
        //pthread_cond_init(&cond,NULL);
     
        sem_init(&full,0,0);
        sem_init(&empty,0,20);
    
        pthread_t tid[8];
    
        int i = 0;
        for(;i < 3;++i)
        {
            pthread_create(&tid[i],NULL,product,(void*)i);
        }
    
        for(i = 3;i < 8;i++)
        {
            pthread_create(&tid[i],NULL,consume,(void*)i);
        }
    
        for(i = 0;i < 8;i++)
        {
            pthread_join(tid[i],NULL);
        }                                                                                                                                 
    
        sem_destroy(&full);
        sem_destroy(&empty);
        return 0;
    }
    

            如果去掉上述两个代码段中有关信号量的操作函数:

    (1)如果生产者的个数比消费者多或生产者的速度大于消费者的速度,此时生产者的优先级比消费者优先级高,会出现以下结果:

    1 product 3
    队列已满
    3 product 3
    队列已满
    4 product 3
    队列已满
    2 product 3
    队列已满
    0 product 3
    ^C
    

            生产者优先级比消费者高,所以生产者一直申请锁,但队列已满,无法向队列中插入结点,而消费者有无法获得锁去消费,所以就造成了生产者的死锁问题。

    (2)如果消费者的个数比生产者多或消费者的速度比生产者快。会出现以下结果:

    5 consume 0
    队列已空
    3 consume 0
    队列已空
    4 consume 0
    队列已空
    7 consume 0
    队列已空
    ^C
    

            消费者优先级比生产者高所以,消费者一直申请锁,但队列已空,无法向从队列中删除结点。而生产者又无法获得锁去生产,所以就造成了消费者的死锁问题。

            所以,要加上上述有关信号量的操作来保证线程同步:

    (1)当队列为满时,不会造成生产者的死锁问题:

    (2)当队列为空时,不会造成消费者的死锁问题:

    3 product 20
    4 product 60
    5 product 43
    6 product 74
    7 consume 20
    2 product 27
    1 product 5
    7 consume 60
    0 product 39
    7 consume 43
    4 product 91
    ^C
    

    2. 利用条件变量来实现同步

            在该模型中还可以通过条件变量来实现线程间的同步。

            当队列为空时,设置一个条件变量,使消费者在该条件下挂起等待,生产者先运行,然后唤醒消费者。当队列为满时,再设置一个条件变量,使生产者在该条件下挂起等待,消费者先运行,然后唤醒生产者。

            具体实现的方法参考上个模型。







            







            







            















    展开全文
  • Java实现生产者消费者模型 生产者消费者模型,是一般面试题都会考的,下面介绍使用ReetrantLock实现 生产者消费者模型。 定义一个ReentrantLock锁,同时new出两个condition,一个控制队满,一个 控制队空 //生产者 ...
  • 使用Java完成的生产者消费者模型,适合初学者对线程进行深入理解使用,本代码仅用于学习交流,祝各位学习顺利!
  • 生产者消费者模型java实现

    万次阅读 2018-09-12 10:33:45
    做题的时候遇到了生产者消费者问题,这个问题可以说是线程学习的经典题目了,就忍不住研究了一波。它描述是有一块缓冲区(队列实现)作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。在Java中这...

    做题的时候遇到了生产者消费者问题,这个问题可以说是线程学习的经典题目了,就忍不住研究了一波。它描述是有一块缓冲区(队列实现)作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。在Java中这个数组线程阻塞的问题,多个用户同时发送多个请求,怎么保证不发生线程死锁,是我们要考虑的问题。

    生产者消费者模式说明:

    1.生产者只在仓库未满时进行生产,仓库满时生产者进程被阻塞;

    2.消费者只在仓库非空时进行消费,仓库为空时消费者进程被阻塞;

    3.当消费者发现仓库为空时会通知生产者生产;

    3.当生产者发现仓库满时会通知消费者消费;

    实现的关键:

    我们知道在JAVA环境中,线程Thread有如下几个状态:

    1.新建状态

    2.就绪状态

    3.运行状态

    4.阻塞状态

    5.死亡状态

    生产者消费者问题就是要控制线程的阻塞状态,保证生产者和消费者进程在一定条件下,一直稳定运行,不出现没有商品但是消费者还是一直购买,商品满了但是生产者还是不断生产导致浪费的情况。

     

    我们考虑线程常用的Sychronized、RetrenLock还有阻塞队列来实现。

    (1)Object的wait() / notify()方法 

    wait(): wait()方法可以让线程进入等待状态,当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。

    notify():notify随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

     

     

    代码实现:

    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Random;
    
    /**
     * 生产者消费者模式:使用Object.wait() / notify()方法实现
     */
    public class ProducerConsumer {
        private static final int CAPACITY = 5;
    //申请一个容量最大的仓库
        public static void main(String args[]){
            Queue<Integer> queue = new LinkedList<Integer>();
    
            Thread producer1 = new Producer("P1", queue, CAPACITY);
            Thread producer2 = new Producer("P2", queue, CAPACITY);
            Thread consumer1 = new Consumer("C1", queue, CAPACITY);
            Thread consumer2 = new Consumer("C2", queue, CAPACITY);
            Thread consumer3 = new Consumer("C3", queue, CAPACITY);
    
            producer1.start();
            producer2.start();
            consumer1.start();
            consumer2.start();
            consumer3.start();
        }
    
        /**
         * 生产者
         */
        public static class Producer extends Thread{
            private Queue<Integer> queue;
           //队列作为仓库
            String name;
            int maxSize;
            int i = 0;
    
            public Producer(String name, Queue<Integer> queue, int maxSize){
                super(name);
                this.name = name;
                this.queue = queue;
                this.maxSize = maxSize;
            }
    
            @Override
            public void run(){
                while(true){
    //while(condition)为自旋锁,为防止该线程没有收到notify()调用也从wait()中返回
    //(也称作虚假唤醒),这个线程会重新去检查condition条件以决定当前是否可以安全
    //地继续执行还是需要重新保持等待,而不是认为线程被唤醒了就可以安全地继续执行
    //了,自旋锁当终止条件满足时,才会停止自旋,这里设置了一直执行,直到程序手动停
    //止。
                    synchronized(queue){
                        //给队列加锁,保证线程安全
                        while(queue.size() == maxSize){
                            //当队列是满的时候,生产者线程等待,由消费者线程进行操作
                            try {
                                System.out .println("Queue is full, Producer[" + name + "] thread waiting for " + "consumer to take something from queue.");
                                queue.wait();
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                        }
                        //队列不为空的时候,生产者被唤醒进行操作
                        System.out.println("[" + name + "] Producing value : +" + i);
                        queue.offer(i++);
                        //因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出一
                       //个 unchecked 异常,而调用 offer() 方法会返回 false
                        queue.notifyAll();
    
                        try {
                            Thread.sleep(new Random().nextInt(1000));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
    
            }
        }
    
        /**
         * 消费者
         */
        public static class Consumer extends Thread{
            private Queue<Integer> queue;
            String name;
            int maxSize;
    
            public Consumer(String name, Queue<Integer> queue, int maxSize){
                super(name);
                this.name = name;
                this.queue = queue;
                this.maxSize = maxSize;
            }
    
            @Override
            public void run(){
                while(true){
                    synchronized(queue){
                        while(queue.isEmpty()){
                            try {
                                //队列为空,说明没有生产者生产的商品,消费者进行等待
                                System.out.println("Queue is empty, Consumer[" + name + "] thread is waiting for Producer");
                                queue.wait();
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                        }
                        int x = queue.poll();
                        //如果队列元素为空,调用remove() 的行为与 Collection 接口的版本相似会抛出异常,这里是模拟消费者取走商品的过程
                        // 但是新的 poll() 方法在用空集合调用时只是返回 null。因此新的方法更适合容易出现异常条件的情况。
                        System.out.println("[" + name + "] Consuming value : " + x);
                        queue.notifyAll();
                         //唤醒所有队列,消费者和生产者根据队列情况进行操作
    
                        try {
                            Thread.sleep(new Random().nextInt(1000));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

     

    2. 使用Lock和Condition的await() / signal()方法

    Condition接口的await()和signal()是用来做同步的两种方法,它们的功能基本上和Object的wait()/ nofity()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。通过在Lock对象上调用newCondition()方法,将条件变量和一个锁对象进行绑定,进而控制并发程序访问竞争资源的安全。

    代码实现:

    import java.util.LinkedList;
    
    import java.util.Queue;
    
    import java.util.Random;
    
    import java.util.concurrent.locks.Condition;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    
    /**
    
    * 生产者消费者模式:使用Lock和Condition实现
    
    
    */
    
    public class ProducerConsumer {
    
    private static final int CAPACITY = 5;
    
    private static final Lock lock = new ReentrantLock();
    
    private static final Condition fullCondition = lock.newCondition();
    
    //队列满的条件
    
    private static final Condition emptyCondition = lock.newCondition();
    
    //队列空的条件
    
    
    public static void main(String args[]){
    
    Queue<Integer> queue = new LinkedList<Integer>();
    
    
    Thread producer1 = new Producer("P1", queue, CAPACITY);
    
    Thread producer2 = new Producer("P2", queue, CAPACITY);
    
    Thread consumer1 = new Consumer("C1", queue, CAPACITY);
    
    Thread consumer2 = new Consumer("C2", queue, CAPACITY);
    
    Thread consumer3 = new Consumer("C3", queue, CAPACITY);
    
    
    producer1.start();
    
    producer2.start();
    
    consumer1.start();
    
    consumer2.start();
    
    consumer3.start();
    
    }
    
    
    /**
    
    * 生产者
    
    */
    
    public static class Producer extends Thread{
    
    private Queue<Integer> queue;
    
    String name;
    
    int maxSize;
    
    int i = 0;
    
    
    public Producer(String name, Queue<Integer> queue, int maxSize){
    
    super(name);
    
    this.name = name;
    
    this.queue = queue;
    
    this.maxSize = maxSize;
    
    }
    
    
    @Override
    
    public void run(){
    
    while(true){
    
    
    //获得锁
    
    lock.lock();
    
    while(queue.size() == maxSize){
    
    try {
    
    System.out .println("Queue is full, Producer[" + name + "] thread waiting for " + "consumer to take something from queue.");
    
    //这里可以和wait()进行对比,两种控制线程阻塞的方式
    
    fullCondition.await();
    
    } catch (InterruptedException ex) {
    
    ex.printStackTrace();
    
    }
    
    }
    
    System.out.println("[" + name + "] Producing value : +" + i);
    
    queue.offer(i++);
    
    
    //唤醒其他所有生产者、消费者
    
    fullCondition.signalAll();
    
    emptyCondition.signalAll();
    
    
    //释放锁,Lock不同于Sychronized,需要手动释放锁
    
    lock.unlock();
    
    try {
    
    Thread.sleep(new Random().nextInt(1000));
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    
    }
    
    
    /**
    
    * 消费者
    
    */
    
    public static class Consumer extends Thread{
    
    private Queue<Integer> queue;
    
    String name;
    
    int maxSize;
    
    
    public Consumer(String name, Queue<Integer> queue, int maxSize){
    
    super(name);
    
    this.name = name;
    
    this.queue = queue;
    
    this.maxSize = maxSize;
    
    }
    
    
    
    @Override
    
    public void run(){
    
    while(true){
    
    //获得锁
    
    lock.lock();
    
    while(queue.isEmpty()){
    
    try {
    
    System.out.println("Queue is empty, Consumer[" + name + "] thread is waiting for Producer");
    
    //队列为空满足条件,消费者线程阻塞
    
    emptyCondition.await();
    
    } catch (Exception ex) {
    
    ex.printStackTrace();
    
    }
    
    }
    
    int x = queue.poll();
    
    System.out.println("[" + name + "] Consuming value : " + x);
    
    
    //唤醒其他所有生产者、消费者
    
    fullCondition.signalAll();
    
    emptyCondition.signalAll();
    
    
    //释放锁
    
    lock.unlock();
    
    
    try {
    
    Thread.sleep(new Random().nextInt(1000));
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    
    }
    
    }
    
    

    (3)BlockingQueue阻塞队列方法 

    我们采用一个阻塞队列来实现。

     

    通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。

    我们这里使用LinkedBlockingQueue,它是一个已经在内部实现了同步的队列,实现方式采用的是我们第2种await()/ signal()方法。它可以在生成对象时指定容量大小。它用于阻塞操作的是put()和take()方法。

    • put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
    • take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。

     

     

    代码实现:

    import java.util.LinkedList;
    
    import java.util.Queue;
    
    import java.util.Random;
    
    import java.util.concurrent.locks.Condition;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    
    /**
    
     * 生产者消费者模式:使用Lock和Condition实现
    
    
     */
    
    public class ProducerConsumer {
    
        private static final int CAPACITY = 5;
    
        private static final Lock lock = new ReentrantLock();
    
        private static final Condition fullCondition = lock.newCondition();
    
    //队列满的条件
    
        private static final Condition emptyCondition = lock.newCondition();
    
    //队列空的条件
    
    
        public static void main(String args[]){
    
            Queue<Integer> queue = new LinkedList<Integer>();
            
            Thread producer1 = new Producer("P1", queue, CAPACITY);
    
            Thread producer2 = new Producer("P2", queue, CAPACITY);
    
            Thread consumer1 = new Consumer("C1", queue, CAPACITY);
    
            Thread consumer2 = new Consumer("C2", queue, CAPACITY);
    
            Thread consumer3 = new Consumer("C3", queue, CAPACITY);
            
            producer1.start();
    
            producer2.start();
    
            consumer1.start();
    
            consumer2.start();
    
            consumer3.start();
    
        }
        
        /**
         * 生产者
         */
        public static class Producer extends Thread{
    
            private Queue<Integer> queue;
    
            String name;
    
            int maxSize;
    
            int i = 0;
    
            public Producer(String name, Queue<Integer> queue, int maxSize){
    
                super(name);
    
                this.name = name;
    
                this.queue = queue;
    
                this.maxSize = maxSize;
    
            }
            
            @Override
    
            public void run(){
    
                while(true){
                //获得锁
                    lock.lock();
    
                    while(queue.size() == maxSize){
                        try {
                            System.out .println("Queue is full, Producer[" + name + "] thread waiting for " + "consumer to take something from queue.");
    
    //这里可以和wait()进行对比,两种控制线程阻塞的方式
    
                            fullCondition.await();
    
                        } catch (InterruptedException ex) {
    
                            ex.printStackTrace();
    
                        }
                    }
    
                    System.out.println("[" + name + "] Producing value : +" + i);
    
                    queue.offer(i++);
    //唤醒其他所有生产者、消费者
                    fullCondition.signalAll();
                    emptyCondition.signalAll();
                    
    //释放锁,Lock不同于Sychronized,需要手动释放锁
    
                    lock.unlock();
                    try {
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
        
        /**
    
         * 消费者
    
         */
    
        public static class Consumer extends Thread{
    
            private Queue<Integer> queue;
    
            String name;
    
            int maxSize;
    
            public Consumer(String name, Queue<Integer> queue, int maxSize){
    
                super(name);
    
                this.name = name;
    
                this.queue = queue;
    
                this.maxSize = maxSize;
    
            }
            
            @Override
    
            public void run(){
    
                while(true){
    
                    lock.lock();
    
                    while(queue.isEmpty()){
                        try {
                            System.out.println("Queue is empty, Consumer[" + name + "] thread is waiting for Producer");
    //队列为空满足条件,消费者线程阻塞
                            emptyCondition.await();
    
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
    
                    }
                    int x = queue.poll();
    
                    System.out.println("[" + name + "] Consuming value : " + x);
                    
                    //唤醒其他所有生产者、消费者
    
                    fullCondition.signalAll();
    
                    emptyCondition.signalAll();
                    
    //释放锁
    
                    lock.unlock();
                    try {
    
                        Thread.sleep(new Random().nextInt(1000));
    
                    } catch (InterruptedException e) {
    
                        e.printStackTrace();
    
                    }
                }
    
            }
    
        }
    
    }
    
    

    小结:三种实现形式,其实理念都是相同的,都是控制阻塞状态,根据条件去控制线程的运行状态和阻塞状态。生产者消费者模式 为信息传输开辟了一个崭新的概念,因为它的优先级最高,所以即使网络发生堵塞时它也会最先通过,最大程度的保证了设备的安全。也有缺点,就是在网络中的个数是有限制的。生产者消费者模式在设置时比较简单,使用方便安全,在将来的自动化行业必定会大大被人们所认同。

    参考资料:

    https://blog.csdn.net/u010983881/article/details/78554671#commentBox

    展开全文
  • Linux实现生产者消费者模型

    万次阅读 2017-06-01 11:35:03
    生产者消费者模型 简单来说就是“321原则(并非某一规则,而是为了理解生产者消费者模型)” “3”代表的是三种关系 生产者与消费者的互斥与同步关系 生产者与生产者的互斥(或竞争)关系 消费者与消费者的...
  • 操作系统 — 生产者消费者模型

    千次阅读 2018-01-21 15:35:34
    生产者消费者模型 所谓的生产者消费者模型就是一个类似于队列一样的东西串起来,这个队列可以想像成一个存放产品的"仓库",生产者只需要关心这个"仓库",并 不需要关心具体的消费...
  •  试想如果没有缓冲区,生产者生产一个数据之后,必须等待消费者消费完成,生产者才能继续生产,那么如果生产者很快,而消费者很慢,那么只能让生产者干等。这好比让CPU和外设直接打交道,如果没有缓存,还不得慢死...
  • 今天小编就为大家分享一篇Python之两种模式的生产者消费者模型详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 主要介绍了Python semaphore evevt生产者消费者模型原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Golang实现生产者消费者模型

    千次阅读 2019-05-30 20:46:02
    代码 package main import ( "fmt" ) func main() { c := make(chan int) go Producer(c) Consumer(c) } func Producer(c chan int) { for i := 0; i < 10; i++ { fmt.Println("Produce:", i) ...- ...
  • Linux生产者消费者模型实现

    千次阅读 2018-11-17 17:08:32
    任何语言提及到多线程同步都离不开生产者/消费者模型。这也是针对许多现实问题建模用到的基础模型。这一篇就来看一下在Linux环境下,C语言实现的两种生产者消费者模型。 关键字:Linux C 生产者 消费者 条件变量...
  • C++ 实现多线程:生产者消费者模型

    千次阅读 2019-09-29 15:14:33
    C++ 实现多线程:生产者消费者模型 #include <iostream> #include <mutex> #include <thread> #ifdef _MSC_VER #include <windows.h> // windows #else #include <unistd.h> // ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 110,876
精华内容 44,350
关键字:

生产者消费者模型