循环链表—libhl库的使用(一)

循环链表—libhl库的使用(一)

本次示例为无锁线程安全循环链表的使用测试。

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 释放内存。如下图:

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注