用C链表实现通用队列Queue

作者: 7aab148ad169 | 来源:发表于2017-04-13 20:50 被阅读110次

队列是最为常见的的数据结构之一,每个合格的程序员都应该掌握如何实现一个队列,这里我讨论如何实现一个线程安全的可阻塞队列的实现。

完整代码请参考jdc_sdl_queue.c

队列结构的定义

struct JDCSDLPacketQueue {
    void *first_pk;//链表头指针
    void *last_pk;//链表尾指针
    int size;//当前size    
    SDL_mutex *mutex;//互斥锁
    SDL_cond *cond;//条件
};

队列的基本操作

经典的队列要要支持几个基本操作

  • Push 从末尾添加元素。
  • pop 从头部移除元素,并返回头部的指针。
  • front 获取头部的指针。
    从队列的定义我们可以推断出使用单向链表来实现是非常方便的。我们需要定义一个链表的结构来就行数据的存储。我的链表node定义如下
typedef struct JDCQueueNode {
    struct JDCQueueNode *next;
    void *data;
}JDCQueueNode;

我将数据指针存在Node的data字段,而data是一个通用指针,我们可以存放任意数据的指针。

让我们来看看队列相关方法的定义.

//为Queue动态分配内存。
JDCSDLPacketQueue *jdc_packet_queue_alloc();
//初始化队列,主要是初始化锁相关的内容。
void jdc_packet_queue_init(JDCSDLPacketQueue *queue);
//获取当前的size
int jdc_packet_queue_size(JDCSDLPacketQueue *queue);
//push
int jdc_packet_queue_push(JDCSDLPacketQueue *queue , void *data);
//front
void *jdc_packet_queue_front(JDCSDLPacketQueue *queue);
//pop
void *jdc_packet_queue_pop(JDCSDLPacketQueue *queue);
//这个方法尝试获取队列头部的元素。Block参数为1时,如果不存在则会阻塞当前线程,直到有元素为止。
int jdc_packet_queue_get_packet(JDCSDLPacketQueue *queue , void **data , int blockThread);

队列的实现

队列的实现主要是对链表的操作,我们先来看看push的操作:

int jdc_packet_queue_push(JDCSDLPacketQueue *queue , void *date)
{
    //分配一个新的链表节点来存储数据
    JDCQueueNode *listNode = (JDCQueueNode *)malloc(sizeof(JDCQueueNode));
    
    if (!listNode) {
        return -1;
    }
    
    listNode->data = data;
    listNode->next = NULL;

    //上锁处理多线程的问题    
    SDL_LockMutex(queue->mutex);
    
    //指针操作,如果是第一个节点直接复制到头指针
    //否则将新节点放到最后一个节点的next    
    if (queue->first_pk == NULL) {
        queue->first_pk = listNode;
    }else{
        ((JDCQueueNode *)queue->last_pk)->next = listNode;
    }

    //更新尾指针 
    queue->last_pk = listNode;
    queue->size ++;
    
    //发信号唤醒等待线程    
    SDL_CondSignal(queue->cond);
    //解锁
    SDL_UnlockMutex(queue->mutex);
    
    return 0;
}

然后是pop操作,将第一个元素拿出来,然后将其从队列中移除:

void *jdc_packet_queue_pop(JDCSDLPacketQueue *queue)
{
    void *data = NULL;
    //加锁
    SDL_LockMutex(queue->mutex);
    
    if (queue->first_pk) {
        //取出第一个元素
        JDCQueueNode *firstPkl = queue->first_pk;
        data = firstPkl->data;
        //更新指针
        queue->first_pk = firstPkl->next;
        queue->size--;
        
        //因为链表node是动态分配内存,需要释放
        free(firstPkl);
    }
    
    SDL_UnlockMutex(queue->mutex);
    
    return data;
}

再来看可阻塞的的获取数据方法:

//这个方法尝试获取队列第一个元素,如果存在则立即返回。如果队列为空而且Block为1的时候会阻塞
int jdc_packet_queue_get_packet(JDCSDLPacketQueue *queue , void **data , int block)
{
    int ret;
    
    SDL_LockMutex(queue->mutex);
    //在这个无限循环中我尝试获取第一个数据
    while (1) {
        if (queue->quit) {
            ret = -1;
            break;
        }
        
        //如果拿到数据立即返回,否则挂起等待信号      
        if (queue->first_pk) {
            *data = jdc_packet_queue_pop(queue);
            ret = 1;
            break;
        }else if(block){
            SDL_CondWait(queue->cond, queue->mutex);
        }else{
            ret = 0;
            break;
        }
    }
    
    SDL_UnlockMutex(queue->mutex);
    
    return ret;
}

总结

这样我们就实现了一个通用的可阻塞等待队列了。我们可以在没有数据的时候先将线程挂起,有数据的时候自动唤醒继续执行。完整代码请参考jdc_sdl_queue.c

相关文章

  • 用C链表实现通用队列Queue

    队列是最为常见的的数据结构之一,每个合格的程序员都应该掌握如何实现一个队列,这里我讨论如何实现一个线程安全的可阻塞...

  • c++ 实现队列

    相关资料: 用C++实现一个队列 数据结构代码实现之队列的链表实现(C/C++)

  • 图(邻接链表)(C语言)

    邻接链表实现图 队列头文件queue.h

  • 优先队列

    优先队列(英语:Priority Queue)Wiki 特点 根据优先级提取数据 往往用链表实现 api及时间复杂...

  • C++ 数据结构 队列Queue学习

    队列Queue 1.queue也是线性表,可以用数组(由于假溢出的原因使用循环数组)和链表来实现和stack不同的...

  • 0225. Implement Stack using Queu

    *LeetCode 225. Implement Stack using Queues用队列来实现链表,对于C语...

  • 用数组实现栈、队列

    用数组实现一个栈 用数组实现一个队列 用单链表实现给队列

  • 队列(链表实现)

    基于链表数据结构实现Queue,将队列表示为一条从最早插入的元素到最近插入的元素的链表,实例变量first指向队列...

  • 多线程GCD

    1:GCD 创建队列: 串行队列: dispatch_queue_t queue=dispatch_queue_c...

  • 有关“队列”的总结

    队列 定义 分类 链式队列 (用链表实现) 静态队列 (用数组实现)图静态队列通常都必须是循环队列循环队列的讲解:...

网友评论

    本文标题:用C链表实现通用队列Queue

    本文链接:https://www.haomeiwen.com/subject/thriattx.html