c語言數據結構實現-數組隊列/環形隊列

一、背景需求

應用場景是一個生產者一個消費者,兩者平均處理速度是相當的,但是有生產峯值速度太快,消費者處理速度跟不上的情況;

這種場景若用單線程處理,則會出現消費速度慢導致拖慢生產者的速度;

若使用雙線程處理,一個生產線程一個消費線程,這個時候就能用到隊列/環形隊列了。

二、相關知識

隊列的數據結構其實非常簡單,實現方式主要爲動態的鏈式結構或者爲靜態的數組結構,本文介紹一種靜態的數組結構;

所示,入列時插入隊列尾,出列時由隊列頭彈出,用下標值進行位置標識。
約束是用戶數據必須是定長的數據,才能用靜態數組結構去標識。

三、實現

創建隊列,需要確定隊列大小以及每個用戶數據的長度
int bufqueue_create(bufqueue_t *phead, size_t nmemb, size_t item_size)
{
    int ix = 0;

    if ( !phead ) {
        return FAILURE;
    }

#define __ISPOWER(_num_) ((_num_ ^ (_num_ - 1)) == (((_num_ - 1) << 1) + 1))
    if ( !__ISPOWER(nmemb) ) {
        return FAILURE;
    }

    /* Refree queue */
    FREE_POINTER(phead->queue);

    phead->queue = (bufqueue_item *)calloc(nmemb, sizeof(bufqueue_item));
    if ( !phead->queue ) {
        return FAILURE;
    }

    for ( ix = 0; ix < nmemb; ix++ ) {
        phead->queue[ix].pitem = calloc(1, item_size);
        if ( !phead->queue[ix].pitem ) {
            return FAILURE;
        }
    }

    phead->item_size = item_size;
    phead->size = nmemb;
    phead->mask = nmemb - 1;
    phead->head = 0;
    phead->tail = 0;
    phead->used = 0;

    printf("Queue alloc success, size: %zd ( Bytes )\n",
            sizeof(bufqueue_t) + nmemb * item_size);

    return SUCCESS;
}

入列操作,分爲獲取隊列尾,返回數據的指針給生成者,生產者生產數據後,更新隊列尾操作
void* bufqueue_tail(bufqueue_t *phead)
{
    if ( SUCCESS == bufqueue_isfull(phead) ) {
        return NULL;
    }
    return (phead->queue[phead->tail].pitem);
}
int bufqueue_push(bufqueue_t *phead)
{
    phead->used++;
    phead->tail = (phead->tail + 1) & phead->mask;

    return SUCCESS;
}

出列,消費者調用該接口直接獲取到數據指針,注意外部並不需要釋放空間
void* bufqueue_pop(bufqueue_t *phead)
{
    void *pitem = NULL;

    if ( phead->tail == phead->head ) {
        return NULL;
    }

    pitem = phead->queue[phead->head].pitem;
    phead->head = (phead->head + 1) & phead->mask;
    phead->used--;

    return (pitem);
}
判定隊列是否爲空
int bufqueue_isempty(bufqueue_t *phead)
{
    if ( phead->tail == phead->head ) {
        return SUCCESS;
    }
    return FAILURE;
}
判斷隊列是否滿
int bufqueue_isfull(bufqueue_t *phead)
{
    if ( !phead ) {
        return FAILURE;
    }

    if ( phead->head == ((phead->tail + 1) & phead->mask) ) {
        /* Queue is full */
        return SUCCESS;
    }
    return FAILURE;
}

四、小結

使用數組隊列類似於犧牲空間換時間的方法,實現簡單。但要求用戶數據必須定長,並且會出現隊列滿的情況;
但是在索引方面,由於使用了下標法進行定位,可以快速地查找到下一個元素(head=(head + 1)&mask),這個時候也必須保證隊列大小爲2^N;
同時在一個生產者一個消費者的場景下,使用該隊列可以無需上鎖,節省鎖的開銷;


發佈了46 篇原創文章 · 獲贊 35 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章