99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

探索字節(jié)隊(duì)列的魔法:多類型支持、函數(shù)重載與線程安全

RT-Thread官方賬號(hào) ? 2024-11-15 01:08 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群


探索字節(jié)隊(duì)列的魔法:多類型支持、函數(shù)重載與線程安全

代碼難度指數(shù):

文章學(xué)習(xí)重點(diǎn):參數(shù)宏的使用技巧

一、引言

嵌入式系統(tǒng)和實(shí)時(shí)應(yīng)用中,數(shù)據(jù)的傳輸和處理是至關(guān)重要的。字節(jié)隊(duì)列(Byte Queue)是一種重要的數(shù)據(jù)結(jié)構(gòu),它能夠高效地存儲(chǔ)和管理數(shù)據(jù)流。通過(guò)使用字節(jié)隊(duì)列,我們可以靈活地處理不同類型的數(shù)據(jù)、確保數(shù)據(jù)的完整性,并在多線程環(huán)境中安全地進(jìn)行操作。本文將深入探討字節(jié)隊(duì)列的概念、作用及其實(shí)現(xiàn)中的多類型支持、函數(shù)重載與線程安全機(jī)制。

1.1 隊(duì)列的基本概念

隊(duì)列是一種先進(jìn)先出(FIFO,F(xiàn)irst In First Out)的數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)通過(guò)“入隊(duì)”(enqueue)操作添加到隊(duì)列的尾部,并通過(guò)“出隊(duì)”(dequeue)操作從隊(duì)列的頭部移除。在嵌入式系統(tǒng)中,隊(duì)列常用于:

  • 數(shù)據(jù)緩沖:在數(shù)據(jù)產(chǎn)生和消費(fèi)速率不匹配的情況下,隊(duì)列可以暫存數(shù)據(jù),平衡輸入和輸出之間的差異。
  • 任務(wù)調(diào)度:任務(wù)或事件的管理可以通過(guò)隊(duì)列來(lái)實(shí)現(xiàn),確保它們按照特定順序被處理。
  • 通信:隊(duì)列可以在不同模塊或線程之間傳遞信息,從而實(shí)現(xiàn)模塊間的解耦和同步。

1.2 字節(jié)隊(duì)列的不足

盡管字節(jié)隊(duì)列在嵌入式系統(tǒng)中提供了基本的數(shù)據(jù)存儲(chǔ)與管理能力,但其在實(shí)際應(yīng)用中也存在一些明顯的不足:

  • 缺乏多類型支持:傳統(tǒng)的字節(jié)隊(duì)列往往只能處理單一類型的數(shù)據(jù),例如,使用固定的字節(jié)數(shù)組存儲(chǔ)數(shù)據(jù),導(dǎo)致不同數(shù)據(jù)類型之間缺乏靈活性。為了支持不同類型的數(shù)據(jù),開(kāi)發(fā)者通常需要?jiǎng)?chuàng)建多個(gè)隊(duì)列,從而增加了代碼的復(fù)雜性和維護(hù)成本。
  • 沒(méi)有函數(shù)重載:在 C 語(yǔ)言中,函數(shù)重載是通過(guò)不同的函數(shù)名稱來(lái)實(shí)現(xiàn)的,缺乏類似 C++的靈活性。這使得在隊(duì)列操作中無(wú)法方便地處理不同數(shù)量和類型的參數(shù),導(dǎo)致代碼冗長(zhǎng)且不易維護(hù)。
  • 線程安全機(jī)制不足:在多線程環(huán)境中,若多個(gè)線程同時(shí)訪問(wèn)字節(jié)隊(duì)列而沒(méi)有適當(dāng)?shù)耐綑C(jī)制,可能會(huì)導(dǎo)致數(shù)據(jù)損壞或不一致。傳統(tǒng)的字節(jié)隊(duì)列實(shí)現(xiàn)往往沒(méi)有內(nèi)置的線程安全支持,增加了并發(fā)編程的難度。

二、字節(jié)隊(duì)列的改進(jìn)

2.1 多類型支持的實(shí)現(xiàn)原理

問(wèn)題: C 語(yǔ)言中的數(shù)組或緩沖區(qū)往往只能存儲(chǔ)單一類型的數(shù)據(jù)。例如,你可以定義一個(gè) uint8_t 數(shù)組來(lái)存儲(chǔ)字節(jié)數(shù)據(jù),或者一個(gè) int32_t 數(shù)組來(lái)存儲(chǔ)整型數(shù)據(jù)。然而,在嵌入式系統(tǒng)中,我們常常需要處理各種類型的數(shù)據(jù)——8 位、16 位、32 位的整數(shù)、浮點(diǎn)數(shù)等等。為了避免為每種類型單獨(dú)創(chuàng)建隊(duì)列,我們希望有一個(gè)靈活的隊(duì)列,可以自動(dòng)支持多種數(shù)據(jù)類型。

解決方案: 我們使用 C 語(yǔ)言的宏來(lái)解決這個(gè)問(wèn)題。通過(guò)宏,隊(duì)列可以自動(dòng)根據(jù)傳入的數(shù)據(jù)類型來(lái)計(jì)算所需的存儲(chǔ)空間。核心思想是:我們不關(guān)心具體的數(shù)據(jù)類型,而是通過(guò)宏和類型推導(dǎo),計(jì)算每個(gè)數(shù)據(jù)需要的字節(jié)數(shù),并按照字節(jié)的形式將數(shù)據(jù)存入隊(duì)列中。

使用 typeof 推斷數(shù)據(jù)類型:

C 語(yǔ)言的 typeof 關(guān)鍵字可以根據(jù)表達(dá)式自動(dòng)推斷出數(shù)據(jù)類型,并可以通過(guò)該類型確定數(shù)據(jù)的大小。在我們的實(shí)現(xiàn)中,隊(duì)列的操作宏會(huì)通過(guò) sizeof 來(lái)獲取傳入數(shù)據(jù)的字節(jié)大小。

示例:

#defineenqueue(queue,data)enqueue_bytes(queue,&data,sizeof(typeof(data)))

在上述宏中:

  • typeof(data) 會(huì)推斷出 data 的類型,然后通過(guò) sizeof(typeof(data))確定該類型占用的字節(jié)數(shù)。
  • 通過(guò)將數(shù)據(jù)的地址傳遞給底層的 enqueue_bytes 函數(shù),我們可以統(tǒng)一將所有類型的數(shù)據(jù)作為字節(jié)流處理。

通過(guò)這種方式,我們的隊(duì)列可以支持任意類型的數(shù)據(jù),比如 8 位字節(jié)、16 位整數(shù)、32 位浮點(diǎn)數(shù),甚至自定義的數(shù)據(jù)結(jié)構(gòu),只要知道它們的大小即可。

2.2 函數(shù)重載的實(shí)現(xiàn)原理

問(wèn)題: 在 C++等語(yǔ)言中,函數(shù)重載允許你定義多個(gè)同名的函數(shù),但參數(shù)類型或數(shù)量不同。然而,C 語(yǔ)言并不原生支持函數(shù)重載。這意味著如果我們想實(shí)現(xiàn)同名函數(shù),處理不同類型或數(shù)量的參數(shù),就需要想出另一種方法。

解決方案: 我們可以通過(guò) C 語(yǔ)言的宏來(lái)“模擬”函數(shù)重載。宏的靈活性使得我們可以根據(jù)不同的參數(shù)數(shù)量或類型,選擇不同的底層函數(shù)進(jìn)行處理。結(jié)合__VAARGS_等可變參數(shù)宏的特性,我們可以輕松實(shí)現(xiàn)這種重載行為。

使用宏實(shí)現(xiàn)參數(shù)數(shù)量的重載: enqueue 可以根據(jù)傳遞的參數(shù)數(shù)量,調(diào)用不同的函數(shù)。我們使用__VAARGS_(可變參數(shù))來(lái)處理不同數(shù)量的參數(shù)。

enqueue 宏的完整代碼如下:

#define__CONNECT3(__A,__B,__C)__A##__B##__C
#define__CONNECT2(__A,__B)__A##__B

#defineCONNECT3(__A,__B,__C)__CONNECT3(__A,__B,__C)
#defineCONNECT2(__A,__B)__CONNECT2(__A,__B)
#defineSAFE_NAME(__NAME)CONNECT3(__,__NAME,__LINE__)

#define__PLOOC_VA_NUM_ARGS_IMPL(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,\
_12,_13,_14,_15,_16,__N,...)__N


#define__PLOOC_VA_NUM_ARGS(...)\
__PLOOC_VA_NUM_ARGS_IMPL(0,##__VA_ARGS__,16,15,14,13,12,11,10,9,\
8,7,6,5,4,3,2,1,0)

#define__ENQUEUE_0(__QUEUE,__VALUE)\
({typeof((__VALUE))SAFE_NAME(value)=__VALUE;\
enqueue_bytes((__QUEUE),&(SAFE_NAME(value)),(sizeof(__VALUE)));})

#define__ENQUEUE_1(__QUEUE,__ADDR,__ITEM_COUNT)\
enqueue_bytes((__QUEUE),(__ADDR),__ITEM_COUNT*(sizeof(typeof((__ADDR[0])))))

#define__ENQUEUE_2(__QUEUE,__ADDR,__TYPE,__ITEM_COUNT)\
enqueue_bytes((__QUEUE),(__ADDR),(__ITEM_COUNT*sizeof(__TYPE)))

#defineenqueue(__queue,__addr,...)\
CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__))\
(__queue,(__addr),##__VA_ARGS__)

工作原理

通過(guò)以上代碼,enqueue宏會(huì)根據(jù)傳遞的參數(shù)數(shù)量,自動(dòng)選擇不同的實(shí)現(xiàn)版本

  • 傳遞的可變參數(shù)如果為 0,調(diào)用__ENQUEUE_0;
  • 傳遞的可變參數(shù)如果為 1,調(diào)用__ENQUEUE_1;
  • 傳遞的可變參數(shù)如果為 2,調(diào)用__ENQUEUE_2。

2.2.1 函數(shù)重載的秘密 ——“__PLOOC_VA_NUM_ARGS”宏的深度剖析

__PLOOC_VA_NUM_ARGS 宏的代碼如下:

#define__PLOOC_VA_NUM_ARGS_IMPL(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,\
_13,_14,_15,_16,__N,...)__N
#define__PLOOC_VA_NUM_ARGS(...)\
__PLOOC_VA_NUM_ARGS_IMPL(0,##__VA_ARGS__,16,15,14,13,12,11,10,9,\
8,7,6,5,4,3,2,1,0)

  1. __PLOOC_VA_NUM_ARGS 宏的作用是它可以告訴我們實(shí)際傳遞了多少個(gè)參數(shù)

這里,首先構(gòu)造了一個(gè)特殊的參數(shù)宏,__PLOOC_VA_NUM_ARGS_IMPL():

  • 在涉及"..."之前,它要用戶至少傳遞 18 個(gè)參數(shù);
  • 這個(gè)宏的返回值就是第十八個(gè)參數(shù)的內(nèi)容;
  • 多出來(lái)的部分會(huì)被"..."吸收掉,不會(huì)產(chǎn)生任何后果

__PLOOC_VA_NUM_ARGS 的巧妙在于,它把__VA_ARGS__放在了參數(shù)列表的最前面,并隨后傳遞了 "16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0" 這樣的序號(hào):

當(dāng)__VA_ARGS__里有 1 個(gè)參數(shù)時(shí),“1”對(duì)應(yīng)第十八個(gè)參數(shù)__N,所以返回值是 1

當(dāng)__VA_ARGS__里有 2 個(gè)參數(shù)時(shí),“2”對(duì)應(yīng)第十八個(gè)參數(shù)__N,所以返回值是 2

...

當(dāng)__VA_ARGS__里有 9 個(gè)參數(shù)時(shí),"9"對(duì)應(yīng)第十八個(gè)參數(shù)__N,所以返回值是 9

舉個(gè)例子:

__PLOOC_VA_NUM_ARGS(0x,D,E,A,D)

展開(kāi)為:

__PLOOC_VA_NUM_ARGS_IMPL(0,0x,D,E,A,D,16,15,14,13,12,11,10,9,\
8,7,6,5,4,3,2,1,0)

__PLOOC_VA_NUM_ARGS 的返回值是 5,從左往右數(shù),第十八個(gè)參數(shù),正好是“5”。

  1. 宏連接符##的作用

#define__CONNECT2(__A,__B)__A##__B
#defineCONNECT2(__A,__B)__CONNECT2(__A,__B)

宏連接符 ## 的主要作用就是連接兩個(gè)字符串,我們?cè)诤甓x中可以使用 ## 來(lái)連接兩個(gè)字符。預(yù)處理器在預(yù)處理階段對(duì)宏展開(kāi)時(shí),會(huì)將## 兩邊的字符合并,并刪除 ## 這兩個(gè)字符。

使用宏連接符 ##要注意一下兩條結(jié)論:

  • 第一條:任何使用到膠水運(yùn)算“##”對(duì)形參進(jìn)行粘合的參數(shù)宏,一定需要額外的再套一層
  • 第二條:其余情況下,如果要用到膠水運(yùn)算,一定要在內(nèi)部借助參數(shù)宏來(lái)完成粘合過(guò)程

為了理解這一“結(jié)論”,我們不妨舉一個(gè)例子:比如定義一個(gè)用于自動(dòng)關(guān)閉中斷并在完成指定操作后自動(dòng)恢復(fù)原來(lái)狀態(tài)的宏:

#defineSAFE_ATOM_CODE(...)\
{\
uint32_twTemp=__disable_irq();\
__VA_ARGS__;\
__set_PRIMASK(wTemp);\
}

由于這里定義了一個(gè)變量 wTemp,而如果用戶插入的代碼中也使用了同名的變量,就會(huì)產(chǎn)生很多問(wèn)題:輕則編譯錯(cuò)誤(重復(fù)定義);重則出現(xiàn)局部變量 wTemp 強(qiáng)行取代了用戶自定義的靜態(tài)變量的情況,從而直接導(dǎo)致系統(tǒng)運(yùn)行出現(xiàn)隨機(jī)性的故障(比如隨機(jī)性的中斷被關(guān)閉后不再恢復(fù),或是原本應(yīng)該被關(guān)閉的全局中斷處于打開(kāi)狀態(tài)等等)。為了避免這一問(wèn)題,我們往往會(huì)想自動(dòng)給這個(gè)變量一個(gè)不會(huì)重復(fù)的名字,比如借助 __LINE__ 宏給這一變量加入一個(gè)后綴:

#defineSAFE_ATOM_CODE(...)\
{\
uint32_twTemp##__LINE__=__disable_irq();\
__VA_ARGS__;\
__set_PRIMASK(wTemp);\
}

假設(shè)這里 SAFE_ATOM_CODE 所在行的行號(hào)是 123,那么我們期待的代碼展開(kāi)是這個(gè)樣子的(我重新縮進(jìn)過(guò)了):

...
{
uint32_twTemp123=__disable_irq();
__VA_ARGS__;
__set_PRIMASK(wTemp);
}
...

然而,實(shí)際展開(kāi)后的內(nèi)容是這樣的:

...
{
uint32_twTemp__LINE__=__disable_irq();
__VA_ARGS__;
__set_PRIMASK(wTemp);
}
...

這里,__LINE__似乎并沒(méi)有被正確替換為 123,而是以原樣的形式與 wTemp 粘貼到了一起——這就是很多人經(jīng)常抱怨的 __LINE__ 宏不穩(wěn)定的問(wèn)題。實(shí)際上,這是因?yàn)樯鲜龊甑臉?gòu)建沒(méi)有遵守前面所列舉的兩條結(jié)論導(dǎo)致的。

從內(nèi)容上看,SAFE_ATOM_CODE() 要粘合的對(duì)象并不是形參,根據(jù)結(jié)論第二條,需要借助另外一個(gè)參數(shù)宏來(lái)幫忙完成這一過(guò)程。為此,我們需要引入一個(gè)專門的宏:

#defineCONNECT2(__A,__B)__A##__B

注意到,這個(gè)參數(shù)宏要對(duì)形參進(jìn)行膠水運(yùn)算,根據(jù)結(jié)論第一條,需要在宏的外面再套一層,因此,修改代碼得到:

#define__CONNECT2(__A,__B)__A##__B
#defineCONNECT2(__A,__B)__CONNECT2(__A,__B)

修改前面的定義得到:

#defineSAFE_ATOM_CODE(...)\
{\
uint32_tCONNECT2(wTemp,__LINE__)=\
__disable_irq();\
__VA_ARGS__;\
__set_PRIMASK(wTemp);\
}

  1. 對(duì) enqueue 的封裝進(jìn)行展開(kāi)

#defineenqueue(__queue,__addr,...)\
CONNECT2(__ENQUEUE_,__PLOOC_VA_NUM_ARGS(__VA_ARGS__))\
(__queue,(__addr),##__VA_ARGS__)

CONNECT2 會(huì)根據(jù)__PLOOC_VA_NUM_ARGS 返回的數(shù)量,與__ENQUEUE_進(jìn)行連接,

  • __PLOOC_VA_NUM_ARGS 返回的數(shù)量如果為 0,調(diào)用__ENQUEUE_0(__queue,(__addr),##__VA_ARGS__);
  • __PLOOC_VA_NUM_ARGS 返回的數(shù)量如果為 1,調(diào)用__ENQUEUE_1(__queue,(__addr),##__VA_ARGS__);
  • __PLOOC_VA_NUM_ARGS 返回的數(shù)量如果為 2,調(diào)用__ENQUEUE_2(__queue,(__addr),##__VA_ARGS__);

舉個(gè)例子:

staticbyte_queue_tmy_queue;
uint8_tdata1=0XAA;
enqueue(&my_queue,data1);//__ENQUEUE_0(&my_queue,data1)
enqueue(&my_queue,&data1,1);//__ENQUEUE_1(&my_queue,&data1,1)
enqueue(&my_queue,&data1,uint8_t,1);//__ENQUEUE_2(&my_queue,&data1,uint8_t,1)
/*__ENQUEUE_0,__ENQUEUE_1,__ENQUEUE_2,展開(kāi)后調(diào)用的都是同一個(gè)接口*/
enqueue_bytes(&my_queue,&data1,1)

2.3 線程安全的實(shí)現(xiàn)原理

問(wèn)題: 在多線程環(huán)境下,如果多個(gè)線程同時(shí)對(duì)同一個(gè)隊(duì)列進(jìn)行操作,可能會(huì)引發(fā)數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,導(dǎo)致數(shù)據(jù)損壞或不一致。為了避免這種情況,我們需要保證每次對(duì)隊(duì)列的操作是原子的,即不可打斷的。

解決方案: 在嵌入式系統(tǒng)中,常用的方法是通過(guò)禁用中斷或使用鎖機(jī)制來(lái)保證數(shù)據(jù)的一致性。在我們的實(shí)現(xiàn)中,我們使用禁用中斷的方式來(lái)確保線程安全。這是一種非常常見(jiàn)的技術(shù),尤其是在實(shí)時(shí)系統(tǒng)中。

為了盡量降低關(guān)中斷對(duì)實(shí)時(shí)性的影響,我們只對(duì)操作隊(duì)列指針的操作進(jìn)行關(guān)中斷保護(hù),相對(duì)耗時(shí)間的數(shù)據(jù)拷貝不進(jìn)行關(guān)中斷。

函數(shù)偽代碼如下:

boolenqueue_bytes(...)
{
boolbEarlyReturn=false;
safe_atom_code(){
if(!this.bMutex){
this.bMutex=true;
}else{
bEarlyReturn=true;
}
}
if(bEarlyReturn){
returnfalse;
}
safe_atom_code(){
/*隊(duì)列指針操作*/
...
}
/*數(shù)據(jù)操作*/
memcpy(...);
...
this.bMutex=false;
returntrue;
}

原子宏 safe_atom_code()的實(shí)現(xiàn):

前邊的例子中,我們實(shí)現(xiàn)了一個(gè) SAFE_ATOM_CODE 的原子宏,唯一的問(wèn)題是,這樣的寫法,在調(diào)試時(shí)完全沒(méi)法在用戶代碼處添加斷點(diǎn)(編譯器會(huì)認(rèn)為宏內(nèi)所有的內(nèi)容都寫在了同一行),這是大多數(shù)人不喜歡使用宏來(lái)封裝代碼結(jié)構(gòu)的最大原因。

接下來(lái)我們用另一種實(shí)現(xiàn)方式來(lái)解決這個(gè)問(wèn)題,代碼如下:

#define__CONNECT3(__A,__B,__C)__A##__B##__C
#define__CONNECT2(__A,__B)__A##__B
#defineCONNECT3(__A,__B,__C)__CONNECT3(__A,__B,__C)
#defineCONNECT2(__A,__B)__CONNECT2(__A,__B)
#defineSAFE_NAME(__NAME)CONNECT3(__,__NAME,__LINE__)
#include"cmsis_compiler.h"
#definesafe_atom_code()\
for(uint32_tSAFE_NAME(temp)=\
({uint32_tSAFE_NAME(temp2)=__get_PRIMASK();\
__disable_irq();\
SAFE_NAME(temp2);}),*SAFE_NAME(temp3)=NULL;\
SAFE_NAME(temp3)++==NULL;\
__set_PRIMASK(SAFE_NAME(temp)))
#endif

工作原理:

safe_atom_code()通過(guò)一個(gè)循環(huán)結(jié)構(gòu)確保在隊(duì)列操作期間,中斷被禁用。循環(huán)結(jié)束后自動(dòng)恢復(fù)中斷。

2.3.1 for 循環(huán)的妙用

首先構(gòu)造一個(gè)只執(zhí)行一次的 for 循環(huán)結(jié)構(gòu):

for(inti=1;i>0;i--){
...
}

對(duì)于這樣的 for 循環(huán)結(jié)構(gòu),幾個(gè)關(guān)鍵部分就有了新的意義:

  • 在執(zhí)行用戶代碼之前(灰色部分),有能力進(jìn)行一定的“準(zhǔn)備工作”(Before 部分);
  • 在執(zhí)行用戶代碼之后,有能力執(zhí)行一定的“收尾工作”(After 部分);
  • 在 init_clause 階段有能力定義一個(gè)“僅僅只覆蓋” for 循環(huán)的,并且只對(duì) User Code 可見(jiàn)的局部變量——換句話說(shuō),這些局部變量是不會(huì)污染 for 循環(huán)以外的地方的。

0f2f2148-a2ab-11ef-8084-92fbcf53809c.png

利用這樣的結(jié)構(gòu),我們很容易就能構(gòu)造出一個(gè)可以通過(guò)花括號(hào)的形式來(lái)包裹用戶代碼的原子操作 safe_atom_code(),在執(zhí)行用戶代碼之前關(guān)閉中斷,在執(zhí)行完用戶代碼之后打開(kāi)中斷,還不影響在用戶代碼中添加斷點(diǎn),單步執(zhí)行。

需要注意的是,如果需要中途退出循環(huán),需要使用continue退出原子操作,不能使用break。

2.4 總結(jié)

通過(guò)上述的多類型支持、函數(shù)重載和線程安全的實(shí)現(xiàn),我們大大增強(qiáng)了字節(jié)隊(duì)列的靈活性和實(shí)用性:

  1. 多類型支持: 自動(dòng)推斷數(shù)據(jù)類型和大小,支持不同類型數(shù)據(jù)的隊(duì)列操作。
  2. 函數(shù)重載: 通過(guò)宏模擬 C 語(yǔ)言的函數(shù)重載,靈活處理不同數(shù)量和類型的參數(shù)。
  3. 線程安全: 通過(guò)禁用中斷機(jī)制確保隊(duì)列操作在多線程環(huán)境中的原子性,避免數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。

這些改進(jìn)使得我們的字節(jié)隊(duì)列不僅可以在單線程環(huán)境中高效運(yùn)行,還能在復(fù)雜的多線程系統(tǒng)中保持?jǐn)?shù)據(jù)的一致性與安全性。

三、API 接口

#definequeue_init(__queue,__buffer,__size,...)\
__PLOOC_EVAL(__QUEUE_INIT_,##__VA_ARGS__)\
(__queue,(__buffer),(__size),##__VA_ARGS__)

#definedequeue(__queue,__addr,...)\
__PLOOC_EVAL(__DEQUEUE_,##__VA_ARGS__)\
(__queue,(__addr),##__VA_ARGS__)

#defineenqueue(__queue,__addr,...)\
__PLOOC_EVAL(__ENQUEUE_,##__VA_ARGS__)\
(__queue,(__addr),##__VA_ARGS__)

#definepeek_queue(__queue,__addr,...)\
__PLOOC_EVAL(__PEEK_QUEUE_,##__VA_ARGS__)\
(__queue,(__addr),##__VA_ARGS__)

extern
byte_queue_t*queue_init_byte(byte_queue_t*ptObj,void*pBuffer,uint16_thwItemSize,boolbIsCover);

extern
boolreset_queue(byte_queue_t*ptObj);

extern
uint16_tenqueue_bytes(byte_queue_t*ptObj,void*pDate,uint16_thwDataLength);

extern
uint16_tdequeue_bytes(byte_queue_t*ptObj,void*pDate,uint16_thwDataLength);

extern
boolis_queue_empty(byte_queue_t*ptQueue);

extern
boolis_peek_empty(byte_queue_t*ptObj);

extern
uint16_tpeek_bytes_queue(byte_queue_t*ptObj,void*pDate,uint16_thwDataLength);

extern
voidreset_peek(byte_queue_t*ptQueue);

extern
voidget_all_peeked(byte_queue_t*ptQueue);

extern
uint16_tget_peek_status(byte_queue_t*ptQueue);

extern
voidrestore_peek_status(byte_queue_t*ptQueue,uint16_thwCount);

extern
uint16_tget_queue_count(byte_queue_t*ptObj);

extern
uint16_tget_queue_available_count(byte_queue_t*ptObj);

四、API 說(shuō)明

  1. 初始化隊(duì)列

queue_init(__queue,__buffer,__size,...)

參數(shù)說(shuō)明:

參數(shù)名描述
__QUEUE隊(duì)列的地址
__BUFFER隊(duì)列緩存的首地址
__BUFFER_SIZE隊(duì)列長(zhǎng)度
可變參數(shù)是否覆蓋,默認(rèn)否
  1. 入隊(duì)

#defineenqueue(__queue,__addr,...)

參數(shù)說(shuō)明:

參數(shù)名描述
__QUEUE隊(duì)列的地址
__ADDR待入隊(duì)的數(shù)據(jù)或者數(shù)據(jù)的地址
...可變參數(shù),需要入隊(duì)的數(shù)據(jù)個(gè)數(shù),或者數(shù)據(jù)類型和個(gè)數(shù),如果為空,則只入隊(duì)一個(gè)數(shù)據(jù)
  1. 出隊(duì)

#definedequeue(__queue,__addr,...)

參數(shù)說(shuō)明:

參數(shù)名描述
__QUEUE隊(duì)列的地址
__ADDR用于保存出隊(duì)數(shù)據(jù)變量的地址
...可變參數(shù),需要出隊(duì)的數(shù)據(jù)個(gè)數(shù),或者數(shù)據(jù)類型和個(gè)數(shù),如果為空,則只出隊(duì)一個(gè)數(shù)據(jù)
  1. 查看

#definepeek_queue(__queue,__addr,...)

參數(shù)說(shuō)明:

參數(shù)名描述
__QUEUE隊(duì)列的地址
__ADDR用于保存查看數(shù)據(jù)變量的地址
...可變參數(shù),數(shù)據(jù)類型和需要查看的數(shù)據(jù)個(gè)數(shù),如果為空,則只查看一個(gè)數(shù)據(jù)

五、快速使用

代碼開(kāi)源地址:https://github.com/Aladdin-Wang/wl_queue

或者打開(kāi)MicroBoot,介紹鏈接:徹底解決單片機(jī)BootLoader升級(jí)程序失敗問(wèn)題,只勾選queue,如圖所示:

0f3a7462-a2ab-11ef-8084-92fbcf53809c.png

使用實(shí)例:

#include"ring_queue.h"

uint8_tdata1=0XAA;
uint16_tdata2=0X55AA;
uint32_tdata3=0X55AAAA55;
uint16_tdata4[]={0x1234,0x5678};
typedefstructdata_t{
uint32_ta;
uint32_tb;
uint32_tc;
}data_t;
data_tdata5={
.a=0X11223344,
.b=0X55667788,
.c=0X99AABBCC,
};

uint8_tdata[100];
staticuint8_ts_hwQueueBuffer[100];
staticbyte_queue_tmy_queue;

queue_init(&my_queue,s_hwQueueBuffer,sizeof(s_hwQueueBuffer));
//根據(jù)變量的類型,自動(dòng)計(jì)算對(duì)象的大小
enqueue(&my_queue,data1);
enqueue(&my_queue,data2);
enqueue(&my_queue,data3);

//一下三種方式都可以正確存儲(chǔ)數(shù)組
enqueue(&my_queue,data4,2);//可以不指名數(shù)據(jù)類型
enqueue(&my_queue,data4,uint16_t,2);//也可以指名數(shù)據(jù)類型
enqueue(&my_queue,data4,uint8_t,sizeof(data4));//或者用其他類型

//一下兩種方式都可以正確存儲(chǔ)結(jié)構(gòu)體類型
enqueue(&my_queue,data5);//根據(jù)結(jié)構(gòu)體的類型,自動(dòng)計(jì)算對(duì)象的大小
enqueue(&my_queue,&data5,uint8_t,sizeof(data5));//也可以以數(shù)組方式存儲(chǔ)

enqueue(&my_queue,(uint8_t)0X11);//常量默認(rèn)為int型,需要強(qiáng)制轉(zhuǎn)換數(shù)據(jù)類型
enqueue(&my_queue,(uint16_t)0X2233);//常量默認(rèn)為int型,需要強(qiáng)制轉(zhuǎn)換數(shù)據(jù)類型
enqueue(&my_queue,0X44556677);
enqueue(&my_queue,(char)'a');//單個(gè)字符也需要強(qiáng)制轉(zhuǎn)換數(shù)據(jù)類型
enqueue(&my_queue,"bc");//字符串默認(rèn)會(huì)存儲(chǔ)空字符\0
enqueue(&my_queue,"def");

//讀出全部數(shù)據(jù)
dequeue(&my_queue,data,get_queue_count(&my_queue));

結(jié)語(yǔ)

本文的目的,告訴大家如何正確的看待宏——宏不是阻礙代碼開(kāi)發(fā)和可讀性的魔鬼

宏不是奇技淫巧

宏可以封裝出其它高級(jí)語(yǔ)言所提供的“基礎(chǔ)設(shè)施”

設(shè)計(jì)良好的宏可以提升代碼的可讀性,而不是破壞它

設(shè)計(jì)良好的宏并不會(huì)影響調(diào)試

宏可以用來(lái)固化某些模板,避免每次都重新編寫復(fù)雜的語(yǔ)法結(jié)構(gòu)

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    C++重載運(yùn)算符和重載函數(shù)詳解

    在計(jì)算機(jī)程序設(shè)計(jì)中,運(yùn)算符重載(英語(yǔ):operator overloading)是多態(tài)的一種。這里,運(yùn)算符(比如+,=或==)被當(dāng)作多態(tài)函數(shù),它們的行為隨著其參數(shù)類型的不同而不同。運(yùn)算符并不一定總是符號(hào)。
    發(fā)表于 09-20 17:14 ?2514次閱讀

    線程編程之二 MFC中的線開(kāi)發(fā)

    線程編程之二 MFC中的線開(kāi)發(fā)五、MFC對(duì)多線程編程的支持  MFC中有兩類線程,分別稱之為工作者
    發(fā)表于 10-22 11:42

    關(guān)于C++中的函數(shù)重載機(jī)制

    重載機(jī)制是一種"假的"多態(tài).(因?yàn)樗窃诰幾g階段就進(jìn)行分配的機(jī)制).另外,C++中還有一種"假的"多態(tài)機(jī)制就是模板機(jī)制,同樣只是改變函數(shù)參數(shù)的類型,并不會(huì)改變函數(shù)具體的實(shí)現(xiàn)方式.
    發(fā)表于 10-01 17:18

    函數(shù)重載隱藏覆蓋的區(qū)別

    參數(shù)類型不同,或者參數(shù)個(gè)數(shù)和參數(shù)類型都不同),返回值類型可相同也可不同;這種情況叫做c++的重載!注意:c語(yǔ)言沒(méi)有函數(shù)
    發(fā)表于 05-11 09:33

    TWEN-ASR ONE 語(yǔ)音識(shí)別系列教程(4)---多線程與消息隊(duì)列使用

    TWEN-ASR ONE 語(yǔ)音識(shí)別系列教程(4)—多線程與消息隊(duì)列使用提示:作者使用 TWEN-ASR ONE V1.0開(kāi)發(fā)板進(jìn)行開(kāi)發(fā)學(xué)習(xí)。文章目錄前言一、多線程的使用與測(cè)試1.1線程
    發(fā)表于 07-02 16:27

    printf()h函數(shù)重載后還需要加入修改項(xiàng)函數(shù)重載

    printf()h函數(shù)重載后,還需要加入下圖的修改項(xiàng)函數(shù)重載時(shí),在自定義頭文件中包含 stdio.h 文件,并加入以下重載
    發(fā)表于 08-23 09:33

    RT-Thread系統(tǒng)消息隊(duì)列常用的函數(shù)接口有哪些

    ,等待消息隊(duì)列線程按照優(yōu)先級(jí)的方式進(jìn)行排列。2. 發(fā)送消息RT-Thread 提供的發(fā)送消息接口函數(shù)有兩種:一種是無(wú)等待超時(shí)接口,一種是有等待超時(shí)。線程或者中斷服務(wù)程序都可以給消息
    發(fā)表于 03-31 14:14

    嵌入式-C++函數(shù)重載

    一、什么是函數(shù)重載 兩個(gè)以上的函數(shù),具有相同的函數(shù)名,通過(guò)參數(shù)的類型和參數(shù)的個(gè)數(shù)不同。編譯器自行匹配,自動(dòng)確定調(diào)用哪一個(gè)
    發(fā)表于 06-28 13:54

    隊(duì)列FIFO——支持網(wǎng)絡(luò)QoS的重要芯片

    摘要:在IP網(wǎng)絡(luò)中支持QoS是近年來(lái)研究的熱點(diǎn),而IDT公司推出的新型存儲(chǔ)器件——隊(duì)列FIFO能夠支持QoS的應(yīng)用。因其具有單器件下支持
    發(fā)表于 03-11 13:22 ?1342次閱讀
    <b class='flag-5'>多</b><b class='flag-5'>隊(duì)列</b>FIFO——<b class='flag-5'>支持</b>網(wǎng)絡(luò)QoS的重要芯片

    線程安全隊(duì)列技術(shù)在交流電機(jī)調(diào)速系統(tǒng)中的應(yīng)用

    介紹了數(shù)據(jù)保護(hù)的概念和原理,根據(jù)交流電機(jī)調(diào)速系統(tǒng)的要求,使用線程安全隊(duì)列技術(shù)對(duì)電機(jī)的數(shù)據(jù)進(jìn)行保護(hù),以期能正確反映電機(jī)的運(yùn)行情況.通過(guò)實(shí)驗(yàn),對(duì)使用線程安全隊(duì)列技術(shù)的
    發(fā)表于 05-25 15:26 ?46次下載
    <b class='flag-5'>線程</b><b class='flag-5'>安全隊(duì)列</b>技術(shù)在交流電機(jī)調(diào)速系統(tǒng)中的應(yīng)用

    什么是線程安全

    線程安全的鏈表-隊(duì)列-棧,就是多線程同時(shí)操作(包括查找、添加、刪除等)鏈表、隊(duì)列或棧,無(wú)論如何操作,就是多
    發(fā)表于 11-17 11:16 ?1次下載

    C++之類中的函數(shù)重載學(xué)習(xí)的總結(jié)

    類的成員函數(shù)之間可以進(jìn)行重載;重載必須發(fā)生在同一個(gè)作用域中;全局函數(shù)和成員函數(shù)不能構(gòu)成重載關(guān)系;
    的頭像 發(fā)表于 12-24 17:02 ?751次閱讀

    uC/OS-II隊(duì)列原理 隊(duì)列相關(guān)函數(shù)介紹

    消息隊(duì)列線程間通訊的一種,主要用作數(shù)據(jù)緩沖,用途非常廣泛。一般情況下遵循先進(jìn)先出原則。
    的頭像 發(fā)表于 09-11 15:04 ?1206次閱讀
    uC/OS-II<b class='flag-5'>隊(duì)列</b>原理 <b class='flag-5'>隊(duì)列</b>相關(guān)<b class='flag-5'>函數(shù)</b>介紹

    如何實(shí)現(xiàn)一個(gè)寫的線程安全的無(wú)鎖隊(duì)列

    加鎖。那么如何實(shí)現(xiàn)一個(gè)寫的線程安全的無(wú)鎖隊(duì)列呢? 互斥鎖:mutexqueue(太簡(jiǎn)單不介紹了) 互斥鎖+條件變量:blockqueu
    的頭像 發(fā)表于 11-08 15:25 ?2011次閱讀
    如何實(shí)現(xiàn)一個(gè)<b class='flag-5'>多</b>讀<b class='flag-5'>多</b>寫的<b class='flag-5'>線程</b><b class='flag-5'>安全</b>的無(wú)鎖<b class='flag-5'>隊(duì)列</b>

    探索虛擬線程:原理與實(shí)現(xiàn)

    虛擬線程的引入與優(yōu)勢(shì) 在Loom項(xiàng)目之前,Java虛擬機(jī)(JVM)中的線程是通過(guò)java.lang.Thread類型來(lái)實(shí)現(xiàn)的,這些線程被稱為平臺(tái)線程
    的頭像 發(fā)表于 06-24 11:35 ?602次閱讀
    <b class='flag-5'>探索</b>虛擬<b class='flag-5'>線程</b>:原理與實(shí)現(xiàn)