环形队列,也可以叫环形缓冲区。我们用数组来举例,通常对于一个放到数组中的队列,生产者将数据写入队尾,消费者从队头取走,两个指针朝着同一个方向运动,消费者追生产者。而“环”在这里的体现,就是不管生产者还是消费者,当指针跑到队尾的时候,掉头到该数组头去,形成一个没有终点的环。
这要做的好处是,当生产者和消费者都是单线程,也就是只有一个生产者和一个消费者的时候,不用加锁。如果生产者或者消费者都是多个,就要加锁了,那还不如用普通队列吧。
- 队空:生产者和消费者的指针在同一位置。
- 队满:生产者的指针比消费者的指针小一个单位的时候,下一步就将指向消费者。
以下是我的示例,采用数组的线性队列,当然你也可以使用链表,这里只说明原理,就不展出了。两个线程分别模拟生产者和消费者
// gcc test.c -lpthread
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#define MAX 10
#define LENGTH 32
struct circular_queue{
char status; /**< 0 队列可写 1队列满*/
int write; /**< 写指针,只能生产者修改 */
int read; /**< 读指针,只能消费者修改 */
char queue[MAX][LENGTH]; /**< 环形队列,每个元素32字节最大长度 */
};
void print_queue(struct circular_queue *cq){
int i = 0;
printf("print all:write=%d,read=%d:", cq->write, cq->read);
for(i = cq->read; i != cq->write; ){
printf("queue[%d]=%s ", i, cq->queue[i]);
i = ++i % MAX;
}
printf("\n");
}
void init_circular_queue(struct circular_queue *cq){
memset(cq, 0, sizeof(struct circular_queue));
}
int put(struct circular_queue *cq, char* str, size_t strlen){
if(cq->status == 1){ // 队列满,无法插入
//perror("full");
print_queue(cq);
return -1;
} else {
memcpy(&cq->queue[cq->write], str, strlen);
cq->queue[cq->write][strlen] = '\0'; // 不要忘了终结符
printf("put queue[%d]=%s\n", cq->write, cq->queue[cq->write]);
cq->write = (cq->write + 1) % MAX;
if((cq->read - 1) == cq->write)
cq->status = 1;
}
return 0;
}
char* get(struct circular_queue *cq){
//printf("test_get:write=%d,read=%d:", cq->write, cq->read);
if(cq->read == cq->write){ // 队列空
return NULL;
} else {
int tmp = cq->read;
cq->read = (cq->read + 1) % MAX;
cq->status = 0;
return cq->queue[tmp];
}
}
void* producer(void *para){
struct circular_queue *cq = (struct circular_queue*)para;
while(1){
put(cq, "abc", 3);
sleep(1);
put(cq, "1", 1);
sleep(1);
put(cq, "123456", 6);
sleep(1);
put(cq, "hello world!", 12);
}
}
void* consumer(void *para){
struct circular_queue *cq = (struct circular_queue*)para;
while(1){
printf("get %s\n", get(cq));
sleep(2);
}
}
int main(){
pthread_t t1, t2;
struct circular_queue cq;
init_circular_queue(&cq);
// 启动生产者线程
if(pthread_create(&t1, NULL, producer, &cq)){
perror("producer");
}
// 启动消费者线程
if(pthread_create(&t2, NULL, consumer, &cq)){
perror("consumer");
}
while(1){}
return 0;
}
运行结果
get (null)
put queue[0]=abc
put queue[1]=1
get abc
put queue[2]=123456
put queue[3]=hello world!
put queue[4]=abc
get 1
put queue[5]=1
put queue[6]=123456
get 123456
put queue[7]=hello world!
put queue[8]=abc
put queue[9]=1
get hello world!
put queue[0]=123456
put queue[1]=hello world!
put queue[2]=abc
get abc
put queue[3]=1
queue full: Success
print all:write=4,read=5:queue[5]=1 queue[6]=123456 queue[7]=hello world! queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1
get 1
put queue[4]=hello world!
queue full: Success
print all:write=5,read=6:queue[6]=123456 queue[7]=hello world! queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world!
queue full: Success
print all:write=5,read=6:queue[6]=123456 queue[7]=hello world! queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world!
get 123456
put queue[5]=123456
queue full: Success
print all:write=6,read=7:queue[7]=hello world! queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world! queue[5]=123456
queue full: Success
print all:write=6,read=7:queue[7]=hello world! queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world! queue[5]=123456
get hello world!
put queue[6]=1
queue full: Success
print all:write=7,read=8:queue[8]=abc queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world! queue[5]=123456 queue[6]=1
get abc
put queue[7]=hello world!
queue full: Success
print all:write=8,read=9:queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world! queue[5]=123456 queue[6]=1 queue[7]=hello world!
queue full: Success
print all:write=8,read=9:queue[9]=1 queue[0]=123456 queue[1]=hello world! queue[2]=abc queue[3]=1 queue[4]=hello world! queue[5]=123456 queue[6]=1 queue[7]=hello world!
get 1
put queue[8]=123456
put queue[9]=hello world!
put queue[0]=abc
get abc
put queue[1]=1
put queue[2]=123456
get 1
put queue[3]=hello world!
put queue[4]=abc
可以看到队列从queue[0]到queue[9]一直在被循环使用,达到了环的目的,而且,不冲突,不用加锁!
创建于2011-09-30 深圳腾讯,更新于 2016-07-08 杭州