一、前言
- 有锁队列
不同于无锁队列,有锁队列是在入列或者出列时,为队列上锁,这样保证代码在运行时同一时间只能有一种操作,因此避免了资源的抢占。 - 使用场景
生产消费者模型之类的场景可以使用。
二、代码演示
-
场景
创建两个工作线程,一个每隔一秒往队列里面写数据,一个每隔三秒从队列往外读取数据,当写入数据达到10次后,线程停止工作。 -
队列头文件定义
我们定义一个USER_INFO
结构体数据类型,里面存储了一些信息,用单向链表的形式在队列中进行存储。
queue.h
代码如下:// // Created by jerry on 2021/4/6. // #ifndef C_QUEUE_DEMO_QUEUE_H #define C_QUEUE_DEMO_QUEUE_H #include <stddef.h> #include <pthread.h> #include <stdbool.h> #include "queue.h" /** * 用户信息 * */ struct USER_INFO { int id; char *name; int age; int sex; }; /** * 链表节点(单向) * 在队列中以此结构存储 * */ struct NODES { struct USER_INFO *userInfo; struct NODES *next; }; /** * 队列句柄 * */ struct QUEUE { size_t qCount; // 队列长度 pthread_mutex_t *qMutex; // Mutex锁 /* 函数指针,初始化后可通过QUEUE->xxx 方式调用 */ void (* ENQUEUE) (struct QUEUE *, struct USER_INFO *); // 入列 struct USER_INFO * (* DEQUEUE) (struct QUEUE *); // 出列 size_t (* QUEUE_COUNT) (struct QUEUE *); // 队列长度 size_t (* QUEUE_COUNT_DEBUG) (struct QUEUE *); // 队列长度 void (* QUEUE_FREE) (struct QUEUE **, bool); // 释放队列 struct NODES *head; // 链表头部 }; /* 队列初始化 */ struct QUEUE *queue_init(); /* 入列 */ void enqueue(struct QUEUE *queue, struct USER_INFO *userInfo); /* 出列 */ struct USER_INFO *dequeue(struct QUEUE *queue); /* 查询队列长度 */ size_t queue_count(struct QUEUE *queue); /* 查询队列长度 */ size_t queue_count_debug(struct QUEUE *queue); /* 释放队列 */ void queue_free(struct QUEUE **queue, bool freeData); #endif //C_QUEUE_DEMO_QUEUE_H
-
定义queue.c文件
此文件详细写出有锁队列是怎样存储数据的,代码如下:
//
// Created by jerry on 2021/4/6.
//
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
#define DEBUG 1
#define LOGD(fmt, ...) {if (DEBUG == 1 ) printf("[D][%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);}
/*************************************************************************
* Function: struct QUEUE *queue_init()
* Description: 初始化队列
* Input: 无
* Return: struct QUEUE * 初始化完成的队列
* Others: NULL
**************************************************************************/
struct QUEUE *queue_init()
{
LOGD("queue init.");
struct QUEUE *queue = (struct QUEUE *)calloc(1, sizeof(struct QUEUE)); // 申请QUEUE空间
queue->qMutex = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t)); // 申请Mutex空间
pthread_mutex_init(queue->qMutex, NULL); // 初始化Mutex
queue->qCount = 0; // 初始化count数值
queue->head = NULL; // 消息节点置空
/* 回调函数指针赋值 */
queue->ENQUEUE = enqueue; // 入列
queue->DEQUEUE = dequeue; // 出列
queue->QUEUE_COUNT = queue_count; // 队列长度
queue->QUEUE_COUNT_DEBUG = queue_count_debug; // 队列长度
queue->QUEUE_FREE = queue_free; // 释放对列
return queue;
}
/*************************************************************************
* Function: void enqueue(struct QUEUE *queue,
* struct USER_INFO *userInfo)
* Description: 数据压入队列
* Input:
* struct QUEUE *queue 队列句柄
* struct USER_INFO *userInfo 需要入列的数据
* Return: NULL
* Others: NULL
**************************************************************************/
void enqueue(struct QUEUE *queue, struct USER_INFO *userInfo)
{
if (queue == NULL)
{
return ;
}
if (userInfo == NULL)
{
return ;
}
pthread_mutex_lock(queue->qMutex); // 上锁
/* 为新来的数据申请空间且赋值 */
struct NODES *newNodes = (struct NODES *)calloc(1, sizeof(struct NODES));
newNodes->userInfo = userInfo; // 赋值
/* 数据入列 */
if (!queue->head)
{
queue->head = newNodes;
newNodes->next = NULL;
}
else
{
struct NODES *tmp = queue->head;
while (tmp->next)
{
tmp = tmp->next;
}
tmp->next = newNodes;
newNodes->next = NULL;
}
++ queue->qCount; // 队列长度 + 1
pthread_mutex_unlock(queue->qMutex); // 解锁
}
/*************************************************************************
* Function: struct USER_INFO *dequeue(struct QUEUE *queue)
* Description: 数据出列
* Input:
* struct QUEUE *queue 队列句柄
* Return: struct USER_INFO * 队列中的数据
* Others: NULL
**************************************************************************/
struct USER_INFO *dequeue(struct QUEUE *queue)
{
if (queue == NULL)
{
return NULL;
}
pthread_mutex_lock(queue->qMutex); // 上锁
/* head为空即队列中没有数据 */
if (!queue->head)
{
pthread_mutex_unlock(queue->qMutex);
return NULL;
}
/* 取出头部数据 */
struct NODES *tmp = queue->head;
queue->head = tmp->next;
tmp->next = NULL;
struct USER_INFO *userInfo = tmp->userInfo;
free(tmp);
tmp = NULL;
-- queue->qCount;
pthread_mutex_unlock(queue->qMutex);
return userInfo;
}
/*************************************************************************
* Function: size_t queue_count(struct QUEUE *queue)
* Description: 获取队列长度
* Input:
* struct QUEUE *queue 队列句柄
* Return: size_t 队列实际长度
* Others: NULL
**************************************************************************/
size_t queue_count(struct QUEUE *queue)
{
if (queue == NULL)
{
return 0;
}
pthread_mutex_lock(queue->qMutex); // 上锁
size_t count = queue->qCount;
pthread_mutex_unlock(queue->qMutex);
return count;
}
/*************************************************************************
* Function: size_t queue_count_debug(struct QUEUE *queue)
* Description: 获取队列长度
* Input:
* struct QUEUE *queue 队列句柄
* Return: size_t 队列实际长度
* Others: NULL
**************************************************************************/
size_t queue_count_debug(struct QUEUE *queue)
{
if (queue == NULL)
{
return 0;
}
pthread_mutex_lock(queue->qMutex); // 上锁
size_t count = 0;
struct NODES *tmp = queue->head;
while (tmp)
{
tmp = tmp->next;
++ count;
}
pthread_mutex_unlock(queue->qMutex);
return count;
}
/*************************************************************************
* Function: void queue_free(struct QUEUE **queue, bool freeData)
* Description: 释放队列
* Input:
* struct QUEUE **queue 队列句柄
* bool freeData 是否需要释放队列中的数据(如果结构体中的数据申请了空间至true)
* Return: NULL
* Others: NULL
**************************************************************************/
void queue_free(struct QUEUE **queue, bool freeData)
{
pthread_mutex_lock((*queue)->qMutex); // 上锁
struct NODES *tmp = NULL;
while ((*queue)->head)
{
tmp = (*queue)->head;
(*queue)->head = tmp->next;
tmp->next = NULL;
if (freeData) // 若tmp->userInfo->name申请过空间,则需要释放,否则freeData至false
{
free(tmp->userInfo->name);
tmp->userInfo->name = NULL;
free(tmp->userInfo);
tmp->userInfo = NULL;
}
free(tmp);
tmp = NULL;
}
/* 释放锁 */
pthread_mutex_unlock((*queue)->qMutex);
pthread_mutex_destroy((*queue)->qMutex);
free((*queue)->qMutex);
(*queue)->qMutex = NULL;
/* 释放整个队列 */
free(*queue);
*queue = NULL;
}
-
使用方法
参考代码:gitee
(1) 初始化队列struct QUEUE *mQueue = NULL; mQueue = queue_init();
(2) 消息入列
static int id = 0; struct USER_INFO *userInfo = (struct USER_INFO *)calloc(1, sizeof(struct USER_INFO)); userInfo->id = id; userInfo->name = get_string(); userInfo->sex = rand() % 2; userInfo->age = rand() % 30; mQueue->ENQUEUE(mQueue, userInfo);
(3) 消息出列
struct USER_INFO *userInfo = mQueue->DEQUEUE(mQueue);
(4) 查询长度
mQueue->QUEUE_COUNT(mQueue); mQueue->QUEUE_COUNT_DEBUG(mQueue);
(5) 释放队列
mQueue->QUEUE_FREE(mQueue, false); mQueue = NULL;
三、代码移植事项
- 可将
struct USER_INFO{}
改成用户需要的数据结构,然后在代码中做相应的替换即可。 - 注意申请的空间要释放,否则会造成内存泄漏。
QUEUE_FREE
中若userInfo->name
是申请的空间,则第二个参数为true
,否则为false
。