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型的大小,所以可以是数据,也可以是指针,可覆盖大部分场景。改进型的队列,对队列元素不在进行限制,队列元素的大小可以设置。