本次示例为无锁线程安全循环链表的使用测试。
libhl开源库编译安装过程,参看往期文章:https://www.madbull.site/?p=897 C语言实现基础数据结构库
测试代码
本次示例设计了一个抢占式任务处理的功能。共1个主线程和5个任务处理线程,主线程向队列 task_q 中推送 300 个任务,5 个任务处理线程,从 task_q 中抢占式的获取任务并处里;并且自己释放处理完的任务的内存。
本次测试还涉及 队列满了新数据对旧数据的覆盖处理 和 队列销毁时内存释放的处理。
创建文件 trqueue.c ,内容如下:
// trqueue.c
// gcc -Wall trqueue.c -o trqueue -lhl
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "rqueue.h"
#define PTH_NUM 5
// 假设这是一个任务
typedef struct task_t_ {
int num ;
} task_t ;
// 声明任务处理函数
void * task_deal(void * arg) ;
// value清理的回调函数,清理申请的内存
void free_task_cb(void *v) ;
// 声明任务处理链表
rqueue_t * task_q = NULL ;
int main(int argc, char *argv[]) {
pthread_t pids[PTH_NUM] ;
int ids[PTH_NUM] ;
int rc ;
// 创建链表。选择覆盖模式,超过链表长度,则把旧数据覆盖掉。
task_q = rqueue_create( 5, RQUEUE_MODE_OVERWRITE ) ;
if( task_q == NULL ) {
return -1 ;
}
// 创建链表值销毁的回调函数,溢出和销毁的时候,对value值的处理
rqueue_set_free_value_callback(task_q, free_task_cb);
// 创建任务处理线程
for (int i=0; i < PTH_NUM ; i++ ) {
ids[i] = i ;
rc = pthread_create(&pids[i], NULL, task_deal, &ids[i]) ;
if (rc != 0 ) {
return -1 ;
}
}
task_t * tsk = NULL ;
int tid = 0 ;
while(tid < 300) {
tsk = (task_t *)calloc(1, sizeof(task_t)) ;
tsk->num = tid++ ;
// 向链表写数据
rqueue_write(task_q, tsk) ;
}
for (int i=0; i < PTH_NUM ; i++ ) {
rc = pthread_join(pids[i], NULL) ;
}
// 销毁链条
rqueue_destroy(task_q);
return 0 ;
}
// 链表销毁时,释放 value 空间
void free_task_cb(void *v) {
if( v ) {
task_t * tsk = (task_t*) v ;
printf("free tsk [%d]\n", tsk->num ) ;
free(v) ;
}
}
// 任务处理函数
void * task_deal(void * arg) {
int id = *(int *) arg ;
task_t * tsk = NULL ;
int flag = 1 ;
while(flag) {
// 判断链表是否为空
if( rqueue_isempty( task_q ) ) {
usleep(20) ;
continue ;
}
// 获取一个数据
tsk = rqueue_read(task_q) ;
if ( tsk == NULL ) {
usleep(20) ;
continue ;
}
if(tsk->num >= 220 ) {
flag = 0 ;
// 大于等于220的任务不处理,扔回任务队列中。
rqueue_write(task_q, tsk) ;
continue ;
}
// 确实拿到了数据
printf("[%d] thread get task [%d]\n", id, tsk->num ) ;
free(tsk) ;
}
pthread_exit(NULL) ;
return NULL ;
}
测试:
下图中,红色框里的数据是因为循环链表中的数据持续增加,没有及时 read 出去导致链表满了,所以清理了旧数据。此次在调用 rqueue_create 创建链表时时给了参数 5 ,为了测试异常处理的情况。
注意:在实际使用过程中,会提前申请足够的空间。肯定是远远超过 5 个缓存的。

程序运行最后80行是在销毁 task_q 时,自动调用回调函数 free_task_cb 释放内存。如下图:

发表回复