v3.0介绍了基于环形队列的生产消费模型,但是只能是单生产者-单消费者。若要支持多生产者-多消费者模式,实现方式有多种,本文介绍一种简单的实现方式,而且只需要在v3.0的基础上稍加改动即可。
目录结构
|-- bin
| `-- mul_pro_mul_con
|-- build
|-- CMakeLists.txt
|-- inc
| `-- ring_queue.h
|-- readme.md
|-- src
| `-- ring_queue.c
`-- test
`-- mul_pro_mul_con.c
ring_queue.h
/***********************************************************************
* Copyright (C) 2020 junfu0903@aliyun.com All rights reserved.
*
* File Name: ring_queue.h
* Brief:
* Author: frank
* Email: junfu0903@aliyun.com
* Version: 4.0
* Created Time:2020-09-11 08:27:33
* Blog: https://blog.csdn.net/weixin_43708622
* Gitee: https://gitee.com/yellow_mamba/c_tutorial
*
***********************************************************************/
#ifndef _RING_QUEUE_H
#define _RING_QUEUE_H
#include <pthread.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define LOCK(x) pthread_mutex_lock(&x) //为了代码的可阅读性,封装了宏,见名知意
#define UNLOCK(x) pthread_mutex_unlock(&x)
#pragma pack(1)
typedef struct _node_s
{
int data;
}node_t;
typedef struct _rq_s
{
int w; //write index
int r; //read index
int node_nums; //node numbers
node_t* pd; //data pointer
pthread_mutex_t lock; //主要改动点,通过在rq_t结构中添加一个互斥锁来支持多生产者多消费者
}rq_t;
#pragma pack(0)
int enqueue(rq_t* rq, node_t* node, int idx);
int dequeue(rq_t* rq, node_t* node, int idx);
rq_t* create_ring_queue(int node_nums);
int destroy_ring_queue(rq_t* rq);
#ifdef __cplusplus
}
#endif
#endif //_RING_QUEUE_H
ring_queue.c
相较于v3.0,接口没有发生变化,只是在相应接口内部增加了锁的处理。
/***********************************************************************
* Copyright (C) 2020 junfu0903@aliyun.com All rights reserved.
*
* File Name: ring_queue.c
* Brief:
* Author: frank
* Email: junfu0903@aliyun.com
* Version: 4.0
* Created Time:2020-09-11 08:38:52
* Blog: https://blog.csdn.net/weixin_43708622
* Gitee: https://gitee.com/yellow_mamba/c_tutorial
*
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "ring_queue.h"
int is_empty(rq_t* rq)
{
return (rq->w == rq->r);
}
int is_full(rq_t* rq)
{
return (((rq->w+1)%rq->node_nums) == rq->r);
}
int enqueue(rq_t* rq, node_t* node, int idx)
{
LOCK(rq->lock);
if (is_full(rq))
{
//printf("tid[%d],queue is full...\n", idx);
UNLOCK(rq->lock);
return 0;
}
rq->pd[rq->w].data = node->data;
rq->w = (rq->w+1)%rq->node_nums;
UNLOCK(rq->lock);
return 1;
}
int dequeue(rq_t* rq, node_t* node, int idx)
{
LOCK(rq->lock);
if (is_empty(rq))
{
//printf("tid[%d],queue is empty...\n", idx);
UNLOCK(rq->lock);
return 0;
}
node->data = rq->pd[rq->r].data;
rq->r = (rq->r+1)%rq->node_nums;
printf("tid[%d],dequeue val=%d\n", idx, node->data);
UNLOCK(rq->lock);
return 1;
}
rq_t* create_ring_queue(int node_nums)
{
rq_t* rq = NULL;
rq = (rq_t*)malloc(sizeof(rq_t));
rq->node_nums = node_nums;
rq->pd = (node_t*)malloc(node_nums*sizeof(node_t));
pthread_mutex_init(&rq->lock, NULL);
return rq;
}
int destroy_ring_queue(rq_t* rq)
{
pthread_mutex_destroy(&rq->lock);
free(rq->pd);
free(rq);
return 0;
}
mul_pro_mul_con.c
为了验证多生产者-多消费者模型的正确性,本文件实现了一个简单的线程池,方便支持多线程的创建。
/***********************************************************************
* Copyright (C) 2020 junfu0903@aliyun.com All rights reserved.
*
* File Name: mul_pro_mul_con.c
* Brief:
* Author: frank
* Email: junfu0903@aliyun.com
* Version: 4.0
* Created Time:2020-09-11 08:30:28
* Blog: https://blog.csdn.net/weixin_43708622
* Gitee: https://gitee.com/yellow_mamba/c_tutorial
*
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "ring_queue.h"
typedef struct _thread_info_t
{
pthread_t tid;
int idx;
void* p_argv;
}thread_info_t;
void* do_produce(void* argv)
{
thread_info_t* p_pthread = (thread_info_t*)argv;
int i = 0;
node_t node;
rq_t* rq = (rq_t*)p_pthread->p_argv;
int idx = p_pthread->idx;
while(1)
{
node.data = i;
if (enqueue(rq, &node, idx))
{
//printf("enqueue node:%d\n", i);
i++;
}
else
sleep(1);
}
return NULL;
}
void* do_consume(void* argv)
{
thread_info_t* p_pthread = (thread_info_t*)argv;
node_t node;
rq_t* rq = (rq_t*)p_pthread->p_argv;
int idx = p_pthread->idx;
while(1)
{
if (dequeue(rq, &node, idx))
;//printf("tid[%d],dequeue val=%d\n", idx, node.data);
else
sleep(1);
}
return NULL;
}
void _start_pthread(int idx, void *(*start_routine) (void *), void* p_argv)
{
if (start_routine == do_produce || start_routine == do_consume)
{
thread_info_t* p_thread_pool = (thread_info_t*)p_argv;
p_thread_pool[idx].tid = -1;
p_thread_pool[idx].idx = idx;
pthread_create(&p_thread_pool[idx].tid, NULL, start_routine, &p_thread_pool[idx]);
}
else
{
printf("unknown routine %p", start_routine);
}
}
int main(int argc, char** argv)
{
int i = 0;
if (argc < 4)
{
printf("Usage: ./mul_pro_mul_con producer_nums consumer_nums node_nums\n");
return -1;
}
int producer_nums = atoi(argv[1]);
int consumer_nums = atoi(argv[2]);
int node_nums = atoi(argv[3]);
printf("sizeof(int)=%ld\n", sizeof(int));
printf("sizeof(node_t*)=%ld\n", sizeof(node_t*));
printf("sizeof(rq_t)=%ld\n", sizeof(rq_t));
rq_t* rq = create_ring_queue(node_nums);
if (NULL == rq)
{
printf("create_ring_queue failed\n");
return -1;
}
thread_info_t* p_producer_pool = (thread_info_t*)malloc(sizeof(thread_info_t)*producer_nums);
for (i = 0; i < producer_nums; ++i)
{
p_producer_pool[i].p_argv = (void*)rq;
_start_pthread(i, do_produce, p_producer_pool);
}
thread_info_t* p_consume_pool = (thread_info_t*)malloc(sizeof(thread_info_t)*consumer_nums);
for (i = 0; i < consumer_nums; ++i)
{
p_consume_pool[i].p_argv = (void*)rq;
_start_pthread(i, do_consume, p_consume_pool);
}
for (i = 0; i < producer_nums; ++i)
pthread_join(p_producer_pool[i].tid, NULL);
for (i = 0; i < consumer_nums; ++i)
pthread_join(p_consume_pool[i].tid, NULL);
destroy_ring_queue(rq);
return 0;
}
源码路径:https://gitee.com/yellow_mamba/c_tutorial/tree/master/queue/v4.0
QQ讨论群:679603305