1. 什么是隊列隊列(queue)是一種只能在一端插入元素、在另一端刪除元素的數據結構,遵循「先入先出」(FIFO)的規則。
隊列中有兩個基本概念:
隊頭指針(可變):永遠指向此隊列的第一個數據元素;
隊尾指針(可變):永遠指向此隊列的最后一個數據元素;
隊列中的數據存儲方式有兩種:
① 基于靜態連續內存(數組)存儲,如圖:② 基于動態內存(鏈表節點)存儲,如圖:
?
后續都使用基于靜態內存存儲的隊列講解。
?隊列提供兩個統一的操作:
「入隊(enqueue)」
入隊將一個元素添加到隊尾,并將隊尾指針+1后移,如圖:
「出隊(dequeue)」
出隊將從隊頭中取出一個元素,并將隊頭指針+1后移,如圖:
2. 環形隊列2.1. 環形隊列的特點
普通隊列的入隊操作將隊尾指針后移+1,出隊操作將隊頭指針后移+1,操作幾次之后會發現隊頭指針和隊尾指針都跑到緩沖區的尾部去了:這就導致了前面的內存空間全被浪費,如果要重新恢復使用,則需要進行元素和指針的移動:顯然這種隊列使用方式太不方便了,所以就誕生了環形隊列:「不用搬移元素和指針,一直可以重復利用這段內存空間」。
2.2. 環形隊列的實現
TencentOS-tiny中環形隊列的實現在tos_ring_queue.h和tos_ring_queue.c中。
typedef struct k_ring_queue_st {
knl_obj_t knl_obj;
uint16_t head; //隊頭指針
uint16_t tail; //隊尾指針
size_t total; //記錄隊列中元素的個數
uint8_t *pool; //隊列底層的存儲結構(一個數組)
size_t item_size; //隊列中每個元素的大小,單位:字節
size_t item_cnt; //隊列中可以容納的元素數量
} k_ring_q_t;
環形隊列初始化,將隊頭指針和隊尾置0:
__API__ k_err_t tos_ring_q_create(k_ring_q_t *ring_q, void *pool, size_t item_cnt, size_t item_size)
{
//省略了參數合法性檢查代碼
ring_q-》head = 0u;
ring_q-》tail = 0u;
ring_q-》total = 0;
ring_q-》pool = (uint8_t *)pool;
ring_q-》item_size = item_size;
ring_q-》item_cnt = item_cnt;
return K_ERR_NONE;
}
判斷環形隊列是否為滿或者為空:
__API__ int tos_ring_q_is_empty(k_ring_q_t *ring_q)
{
TOS_CPU_CPSR_ALLOC();
int is_empty = K_FALSE;
//省略了參數合法性檢查代碼
TOS_CPU_INT_DISABLE();
is_empty = (ring_q-》total == 0 ? K_TRUE : K_FALSE);
TOS_CPU_INT_ENABLE();
return is_empty;
}
__API__ int tos_ring_q_is_full(k_ring_q_t *ring_q)
{
TOS_CPU_CPSR_ALLOC();
int is_full = K_FALSE;
//省略了參數合法性檢查代碼
TOS_CPU_INT_DISABLE();
is_full = (ring_q-》total == ring_q-》item_cnt ? K_TRUE : K_FALSE);
TOS_CPU_INT_ENABLE();
return is_full;
}
環形隊列入隊操作的API如下:
__API__ k_err_t tos_ring_q_enqueue(k_ring_q_t *ring_q, void *item, size_t item_size);
在此API中,入隊操作的實現如下:
__STATIC_INLINE__ void ring_q_item_increase(k_ring_q_t *ring_q)
{
ring_q-》tail = RING_NEXT(ring_q, ring_q-》tail);
++ring_q-》total;
}
環形隊列出隊操作的API如下:
__API__ k_err_t tos_ring_q_dequeue(k_ring_q_t *ring_q, void *item, size_t *item_size);
在此API中,出隊操作的實現如下:
__STATIC_INLINE__ void ring_q_item_decrease(k_ring_q_t *ring_q)
{
ring_q-》head = RING_NEXT(ring_q, ring_q-》head);
--ring_q-》total;
}
在入隊和出隊操作的時候都使用了 RING_NEXT 宏,用來獲取在環形隊列中的下一個位置:
#define RING_NEXT(ring_q, index) ((index + 1) % ring_q-》item_cnt)
2.3. 環形隊列使用Demo
編寫如下的測試代碼:
#include 《tos_k.h》typedef struct item_st {
int a;
int b;
int c;
} item_t;
#define RING_QUEUE_ITEM_MAX 5uint8_t ring_q_buffer[RING_QUEUE_ITEM_MAX * sizeof(item_t)];
k_ring_q_t ring_q;
void entry_task_demo(void *arg)
{
k_err_t err;
int i;
item_t item;
size_t item_size;
//創建環形隊列
tos_ring_q_create(&ring_q, ring_q_buffer, RING_QUEUE_ITEM_MAX, sizeof(item_t));
//數據入隊
for(i = 0;i 《 RING_QUEUE_ITEM_MAX; i++)
{
item.a = i;
item.b = i;
item.c = i;
err = tos_ring_q_enqueue(&ring_q, &item, sizeof(item_t));
if(err == K_ERR_NONE)
{
printf(“enqueue a item: %d %d %d
”, item.a, item.b, item.c);
}
else
{
printf(“ring queue enqueue fail,err = %d
”, err);
}
}
//隊列滿之后,繼續入隊
err = tos_ring_q_enqueue(&ring_q, &item, sizeof(item_t));
if(err == K_ERR_RING_Q_FULL)
{
printf(“ring queue is full: %s
”, tos_ring_q_is_full(&ring_q) ? “TRUE” : “FALSE”);
}
else
{
printf(“ring queue enqueue fail,err = %d
”, err);
}
//數據出隊
for(i = 0; i 《 RING_QUEUE_ITEM_MAX; ++i)
{
err = tos_ring_q_dequeue(&ring_q, &item, &item_size);
if(err == K_ERR_NONE)
{
printf(“dequeue a item(%d bytes): %d %d %d
”, item_size, item.a, item.b, item.c);
}
else
{
printf(“ring queue dequeue fail,err = %d
”, err);
}
}
//沒有數據后繼續出隊
err = tos_ring_q_dequeue(&ring_q, &item, &item_size);
if(err == K_ERR_RING_Q_EMPTY)
{
printf(“ring queue is empty: %s
”, tos_ring_q_is_empty(&ring_q) ? “TRUE” : “FALSE”);
}
else
{
printf(“ring queue dequeue fail,err = %d
”, err);
}
}
運行結果如下:
3. 優先級隊列3.1. 優先級隊列的特點
優先級隊列也是一種基于隊列的數據結構,但是它「不遵循FIFO」,而是按照每個元素的優先級進行出隊:「最高優先級的先出隊」。
3.2. 優先級隊列的實現
TencentOS-tiny中環形隊列的實現在tos_prio_queue.h和tos_prio_queue.c中。
優先級隊列在數據入隊的時候,會按照入隊元素的優先級進行一次排序,「將優先級值最小(優先級最高的元素)放在隊頭」,出隊的時候只需要取第一個元素即可。
正是因為這種特性,優先級隊列的底層存儲結構不能使用數組(排序太麻煩),而是使用了二項堆的數據結構。
?
二項堆是一種二叉樹集合的數據結構,在本文中不再深入講解,有興趣的讀者可以自己搜索閱讀。
?下面只給出優先級隊列的API,「理解其規則,會用即可」。
創建優先級隊列
__API__ k_err_t tos_prio_q_create(k_prio_q_t *prio_q, void *mgr_array, void *pool, size_t item_cnt, size_t item_size);
參數描述
prio_q優先級隊列控制塊指針
mgr_array提供一塊緩沖區用于內部管理
pool隊列的緩沖區
item_cnt隊列可容納的元素數量
item_size每個元素的大小,單位字節
其中用于內部管理的緩存區大小可以使用宏定義來計算,比如有5個元素的管理緩沖區大小:
uint8_t mgr_pool[TOS_PRIO_Q_MGR_ARRAY_SIZE(5)];
元素入隊
__API__ k_err_t tos_prio_q_enqueue(k_prio_q_t *prio_q, void *item, size_t item_size, k_prio_t prio);
其中優先級的值遵循:數值越小,優先級越高。
元素出隊
__API__ k_err_t tos_prio_q_dequeue(k_prio_q_t *prio_q, void *item, size_t *item_size, k_prio_t *prio);
其中prio需要傳入一個地址,用于記錄出隊元素的優先級。
3.3. 優先級隊列使用Demo
#include 《tos_k.h》typedef struct item_st {
int a;
int b;
int c;
} item_t;
#define PRIO_QUEUE_ITEM_MAX 5uint8_t prio_q_buffer[PRIO_QUEUE_ITEM_MAX * sizeof(item_t)];
uint8_t mgr_pool[TOS_PRIO_Q_MGR_ARRAY_SIZE(PRIO_QUEUE_ITEM_MAX)];
k_prio_q_t prio_q;
void entry_task_demo(void *arg)
{
k_err_t err;
int i;
item_t item;
size_t item_size;
k_prio_t item_prio;
//創建優先級隊列
tos_prio_q_create(&prio_q, mgr_pool, prio_q_buffer, PRIO_QUEUE_ITEM_MAX, sizeof(item_t));
//數據入隊
for(i = PRIO_QUEUE_ITEM_MAX;i 》 0; i--)
{
item.a = i;
item.b = i;
item.c = i;
err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item), i);
if(err == K_ERR_NONE)
{
printf(“enqueue a item: %d %d %d
”, item.a, item.b, item.c);
}
else
{
printf(“prio queue enqueue fail,err = %d
”, err);
}
}
//隊列滿之后,繼續入隊
err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item_t), i);
if(err == K_ERR_PRIO_Q_FULL)
{
printf(“prio queue is full: %s
”, tos_prio_q_is_full(&prio_q) ? “TRUE” : “FALSE”);
}
else
{
printf(“prio queue enqueue fail,err = %d
”, err);
}
//數據出隊
for(i = 0; i 《 PRIO_QUEUE_ITEM_MAX; ++i)
{
err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &item_prio);
if(err == K_ERR_NONE)
{
printf(“dequeue a item[piro %d]: %d %d %d
”, item_prio, item.a, item.b, item.c);
}
else
{
printf(“prio queue dequeue fail,err = %d
”, err);
}
}
//沒有數據后繼續出隊
err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &item_prio);
if(err == K_ERR_PRIO_Q_EMPTY)
{
printf(“prio queue is empty: %s
”, tos_prio_q_is_empty(&prio_q) ? “TRUE” : “FALSE”);
}
else
{
printf(“prio queue dequeue fail,err = %d
”, err);
}
}
4. 總結① 普通隊列是一種只能在一端入隊,在一端出隊的數據結構,規則:FIFO。
② 環形隊列對內存空間的利用率最高,使用最多,規則:FIFO。
③ 優先級隊列不遵循FIFO,每個元素都有自己的優先級,規則:優先級最高的元素先出隊。
責任編輯:haq
-
數據
+關注
關注
8文章
7139瀏覽量
89576 -
存儲
+關注
關注
13文章
4353瀏覽量
86171 -
TencentOS
+關注
關注
0文章
8瀏覽量
7329
原文標題:TencentOS-tiny中隊列、環形隊列、優先級隊列的實現及使用
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論