queue .h

#ifndef __QUEUE_H__
#define __QUEUE_H__

/*
* -lpthread
* 编译时需要线程库
*/
#include <pthread.h>


/*
* https://www.kernel.org/doc/man-pages/
* https://man7.org/linux/man-pages/man3/pthread_cond_timedwait.3p.html
*/
struct queue_buf_t {
long *data_buf;
int size;
int wr;
int rd;
pthread_mutex_t mtx;

/*
* 数据成功入队后,触发pop_cond,去唤醒由于对空队列进行pop操作被阻塞的线程
*/
pthread_cond_t pop_cond;

/*
* 数据成功出队后,触发push_cond,去唤醒由于满空队列进行push操作被阻塞的线程
*/
pthread_cond_t push_cond;
};



int queue_buf_size(struct queue_buf_t *queue);
int queue_buf_num(struct queue_buf_t *queue);
int queue_buf_push(struct queue_buf_t *queue, long data);
int queue_buf_push_wait(struct queue_buf_t *queue, long data, unsigned long timeout_ms);
int queue_buf_pop(struct queue_buf_t *queue, long *data);
int queue_buf_pop_wait(struct queue_buf_t *queue, long *data, unsigned long timeout_ms);
int queue_buf_get(struct queue_buf_t *queue, long *data);
int queue_buf_get_wait(struct queue_buf_t *queue, long *data, unsigned long timeout_ms);

struct queue_buf_t *queue_buf_alloc(int size);
void queue_buf_free(struct queue_buf_t *queue_buf);


#endif

 queue.c

/*
* Copyright (C) 2021, 2021 huohongpeng
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Change logs:
* Date Author Notes
* 2021-05-15 huohongpeng 首次添加
* 2021-05-17 修改tm_to_ns()在32bit平台溢出问题
*
*/


#include "queue.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>


static long long tm_to_ns(struct timespec tm)
{
long long ret = tm.tv_sec;
ret = ret * 1000000000 + tm.tv_nsec;

return ret;
}

static struct timespec ns_to_tm(long long ns)
{
struct timespec tm;
long long tmp;
tmp = ns / 1000000000;
tm.tv_sec = tmp;

tm.tv_nsec = ns - (tmp * 1000000000);
return tm;
}


int queue_buf_size(struct queue_buf_t *queue)
{
return queue->size - 1;
}

int queue_buf_num(struct queue_buf_t *queue)
{
/*
* 情况1: wr > rd, num = wr - rd;
* 情况2: wr < rd, num = size - rd + wr;
* 情况3: wr == rd, num = 0; 队列是空
* 情况4: (wr+1) % size == rd; 队列满
* 情况1和情况2可以合并为: (size -rd + wr) % size
*/

pthread_mutex_lock(&queue->mtx);
int num = (queue->size - queue->rd + queue->wr) % queue->size;
pthread_mutex_unlock(&queue->mtx);

return num;
}

int queue_buf_push(struct queue_buf_t *queue, long data)
{
if (!queue) {
return -1;
}

int ret = 0;

pthread_mutex_lock(&queue->mtx);
if ((queue->wr + 1) % queue->size == queue->rd) {
ret = -1;
}

if (ret == 0) {
queue->data_buf[queue->wr] = data;
queue->wr++;
queue->wr %= queue->size;
}

pthread_mutex_unlock(&queue->mtx);

if (ret == 0) {
/*
* 队列中有数据了,通知其他被阻塞的线程可以读数据
*/
pthread_cond_signal(&queue->pop_cond);
}

return ret;
}


int queue_buf_push_wait(struct queue_buf_t *queue, long data, unsigned long timeout_ms)
{
if (!queue) {
return -1;
}

int ret = 0;

struct timespec start_tm;
struct timespec end_tm;

clock_gettime(CLOCK_MONOTONIC, &start_tm);

long long tmp = timeout_ms;
end_tm = ns_to_tm(tm_to_ns(start_tm) + tmp*1000000);

pthread_mutex_lock(&queue->mtx);

while ((queue->wr + 1) % queue->size == queue->rd) {
/*
* 队列为满需要等待push_cond有效
*/
if (pthread_cond_timedwait(&queue->push_cond, &queue->mtx, &end_tm) == ETIMEDOUT) {
/*
* 如果超时则退出等待
*/
ret = -1;
break;
}
}

if (ret == 0) {
queue->data_buf[queue->wr] = data;
queue->wr++;
queue->wr %= queue->size;
}

pthread_mutex_unlock(&queue->mtx);

if (ret == 0) {
pthread_cond_signal(&queue->pop_cond);
}

return ret;
}


int queue_buf_pop(struct queue_buf_t *queue, long *data)
{
if (!queue) {
return -1;
}

int ret = 0;

pthread_mutex_lock(&queue->mtx);

if (queue->rd == queue->wr) {
ret = -1;
}

if (ret == 0) {
*data = queue->data_buf[queue->rd];
queue->rd++;
queue->rd %= queue->size;
}

pthread_mutex_unlock(&queue->mtx);

if (ret == 0) {
/*
* 通知其他线程队列已经有空间
*/
pthread_cond_signal(&queue->push_cond);
}

return ret;
}


int queue_buf_pop_wait(struct queue_buf_t *queue, long *data, unsigned long timeout_ms)
{
if (!queue) {
return -1;
}

int ret = 0;

struct timespec start_tm;
struct timespec end_tm;

clock_gettime(CLOCK_MONOTONIC, &start_tm);
long long tmp = timeout_ms;
end_tm = ns_to_tm(tm_to_ns(start_tm) + tmp*1000000);

pthread_mutex_lock(&queue->mtx);

while (queue->rd == queue->wr) {
/*
* 队列为空需要等待pop_cond有效
*/
if (pthread_cond_timedwait(&queue->pop_cond, &queue->mtx, &end_tm) == ETIMEDOUT) {
/*
* 如果超时则退出等待
*/
ret = -1;
break;
}
}

if (ret == 0) {
*data = queue->data_buf[queue->rd];
queue->rd++;
queue->rd %= queue->size;
}

pthread_mutex_unlock(&queue->mtx);

if (ret == 0) {
/*
* 通知其他线程队列已经有空间
*/
pthread_cond_signal(&queue->push_cond);
}

return ret;
}


int queue_buf_get(struct queue_buf_t *queue, long *data)
{
if (!queue) {
return -1;
}

int ret = 0;

pthread_mutex_lock(&queue->mtx);

if (queue->rd == queue->wr) {
ret = -1;
}

if (ret == 0) {
*data = queue->data_buf[queue->rd];
}

pthread_mutex_unlock(&queue->mtx);

return ret;

}


int queue_buf_get_wait(struct queue_buf_t *queue, long *data, unsigned long timeout_ms)
{
if (!queue) {
return -1;
}

int ret = 0;

struct timespec start_tm;
struct timespec end_tm;

clock_gettime(CLOCK_MONOTONIC, &start_tm);
long long tmp = timeout_ms;
end_tm = ns_to_tm(tm_to_ns(start_tm) + tmp*1000000);

pthread_mutex_lock(&queue->mtx);

while (queue->rd == queue->wr) {
/*
* 队列为空需要等待pop_cond有效
*/
if (pthread_cond_timedwait(&queue->pop_cond, &queue->mtx, &end_tm) == ETIMEDOUT) {
/*
* 如果超时则退出等待
*/
ret = -1;
break;
}
}

if (ret == 0) {
*data = queue->data_buf[queue->rd];
}

pthread_mutex_unlock(&queue->mtx);

return ret;
}


struct queue_buf_t *queue_buf_alloc(int size)
{
struct queue_buf_t *queue;
char *p;

/*
* n个buffer最多存储n-1个元素,所以存储size个元素需要size+1个buffer
*/
size = size + 1;

p = (char *)malloc(sizeof(struct queue_buf_t) + size * sizeof(long));

if (!p) {
return NULL;
}

queue = (struct queue_buf_t *)p;
memset(queue, 0x00, sizeof(struct queue_buf_t));

queue->data_buf = (long *)(p + sizeof(struct queue_buf_t));
queue->size = size;

pthread_mutex_init(&queue->mtx, NULL);

pthread_condattr_t attr;
pthread_condattr_init(&attr);

#if 0
clockid_t clock_id;
pthread_condattr_getclock(&attr, &clock_id);
printf("clock_id: %d\n", clock_id);
#endif

/*
* pthread_cond_timedwait()默认使用的是CLOCK_REALTIME,
* CLOCK_REALTIME容易受系统影响,比如校时操作
* 所以条件变量使用的时钟改为CLOCK_MONOTONIC
* 参考:https://man7.org/linux/man-pages/man3/pthread_cond_timedwait.3p.html
*/
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);

pthread_cond_init(&queue->push_cond, &attr);
pthread_cond_init(&queue->pop_cond, &attr);

pthread_condattr_destroy(&attr);

return queue;
}


void queue_buf_free(struct queue_buf_t *queue)
{
if (queue) {
pthread_mutex_destroy(&queue->mtx);
pthread_cond_destroy(&queue->pop_cond);
pthread_cond_destroy(&queue->push_cond);
free(queue);
}
}

//#define __TEST__
#ifdef __TEST__

#include "log.h"
#include <unistd.h>


pthread_t thread_push;
pthread_t thread_pop;
struct queue_buf_t *queue_buf;

void * pop_routine(void* para)
{
long data;

while (1) {

u_tm_log("pop: start...\n");
int ret = queue_buf_pop_wait(queue_buf, &data, 500);
//int ret = queue_buf_pop(queue_buf, &data);
if(ret == 0) {
u_tm_log("pop: %ld\n", data);
} else {
u_tm_log("pop: timeout\n");
}

sleep(1);
}
}


void * push_routine(void* para)
{
while (1) {
u_tm_log("push: start...\n");
int ret = queue_buf_push_wait(queue_buf, 666, 100);
//int ret = queue_buf_push(queue_buf, 66);
if (ret == 0) {
u_tm_log("push: num = %d\n", queue_buf_num(queue_buf));
} else {
u_tm_log("push: timeout\n");
}
usleep(500*1000);
}
}


void test3(void)
{
queue_buf = queue_buf_alloc(10);

pthread_create(&thread_push, NULL, push_routine, NULL);
pthread_create(&thread_pop, NULL, pop_routine, NULL);

sleep(2000);

queue_buf_free(queue_buf);
}


int main()
{
test3();
}

#endif

编译

gcc queue.c -Wall -lpthread

队列改进:

本文实现的队列,可以传输一个long型的大小,所以可以是数据,也可以是指针,可覆盖大部分场景。改进型的队列,对队列元素不在进行限制,队列元素的大小可以设置。