微信公眾號(hào) | strongerHuang
進(jìn)程和線程我們都很熟悉了,在RTOS系統(tǒng)中的叫法可能不一樣,我們熟悉的就是任務(wù)(Task),這個(gè)和線程(Thread)比較近似,你會(huì)發(fā)現(xiàn)有些地方RTOS的任務(wù),也叫線程。
但是在RTOS中還有一種不是那么常見(jiàn)的程序,叫協(xié)程。今天就來(lái)簡(jiǎn)單聊聊RTOS中協(xié)程和任務(wù)的內(nèi)容,以及它們的區(qū)別。
什么是協(xié)程?
協(xié)程,是協(xié)同程序的縮寫(xiě),英文名Coroutine。
協(xié)程是一種比線程更加輕量級(jí)的并發(fā)編程模型、程序組件,它允許單個(gè)線程內(nèi)執(zhí)行多個(gè)協(xié)程,而這些協(xié)程可以在執(zhí)行過(guò)程中掛起和恢復(fù),從而實(shí)現(xiàn)并發(fā)執(zhí)行的效果。
這里通過(guò)一張圖來(lái)了解下進(jìn)程、線程和協(xié)程的關(guān)系:
協(xié)程主要由三種狀態(tài):運(yùn)行(Runing)、就緒(Ready)、阻塞(Blocked)。
運(yùn)行:當(dāng)協(xié)程實(shí)際執(zhí)行時(shí),它被稱(chēng)為處于運(yùn)行狀態(tài),當(dāng)前協(xié)程正在使用處理器。
就緒:就緒的協(xié)程是那些能夠執(zhí)行(未阻塞)但目前未執(zhí)行的協(xié)程。
阻塞:如果協(xié)程當(dāng)前正在等待時(shí)間事件或外部事件,則該協(xié)程被稱(chēng)為處于阻塞狀態(tài) 。
協(xié)程的函數(shù)結(jié)構(gòu):
void vACoRoutineFunction( CoRoutineHandle_t xHandle,
UBaseType_t uxIndex )
{
crSTART( xHandle );
for( ;; )
{
//-- Co-routine application code here. --
}
crEND();
}
以調(diào)用 crSTART() 開(kāi)始,調(diào)用 crEND() 結(jié)束,協(xié)程函數(shù)不應(yīng)返回任何值。
它其實(shí)和RTOS中的任務(wù)有點(diǎn)類(lèi)似,但也有很多不同(最后說(shuō)區(qū)別)。
協(xié)程的案例
上面通過(guò)文字描述可能對(duì)于很多新手有點(diǎn)抽象,也有點(diǎn)難理解,這里結(jié)合代碼案例給大家描述協(xié)程。
1、創(chuàng)建一個(gè)簡(jiǎn)單的LED閃爍協(xié)程?
void vFlashCoRoutine( CoRoutineHandle_t xHandle,
UBaseType_t uxIndex )
{
crSTART( xHandle );
for( ;; )
{
????????//?延時(shí)一段時(shí)間
crDELAY( xHandle, 10 );
????????//?閃爍(翻轉(zhuǎn)LED)
vParTestToggleLED( 0 );
}
crEND();
}
2、調(diào)度協(xié)程
通過(guò)調(diào)用 vCoRoutineSchedule() 來(lái)調(diào)度協(xié)程。可以在任務(wù)空閑的時(shí)候:
void vApplicationIdleHook( void )
{
vCoRoutineSchedule( void );
}
或者在沒(méi)有執(zhí)行其他函數(shù)的時(shí)候,循環(huán)調(diào)用:
void vApplicationIdleHook( void )
{
for( ;; )
{
vCoRoutineSchedule( void );
}
}
3、創(chuàng)建協(xié)程并啟動(dòng)任務(wù)調(diào)度器
比如:在 main() 函數(shù)中創(chuàng)建并啟動(dòng)調(diào)度器。
#include "task.h"
#include "croutine.h"
#define PRIORITY_0 0
void main( void )
{
????//?創(chuàng)建協(xié)程
????xCoRoutineCreate(?vFlashCoRoutine,?PRIORITY_0,?0?);
????//啟用調(diào)度器.
vTaskStartScheduler();
}
4、擴(kuò)展:使用索引參數(shù)
假設(shè)我們要從同一函數(shù)中創(chuàng)建 8 個(gè)這樣的協(xié)程。每個(gè)協(xié)程將 以不同速度閃爍不同的 LED。索引參數(shù)可用于在協(xié)程函數(shù)中區(qū)分協(xié)程。
這里,我們創(chuàng)建 8 個(gè)協(xié)程,并向每個(gè)協(xié)程傳遞不同的索引:
#include "task.h"
#include "croutine.h"
#define PRIORITY_0 0
#define NUM_COROUTINES 8
void main( void )
{
int i;
for( i = 0; i < NUM_COROUTINES; i++ )
{
// This time i is passed in as the index.
xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, i );
}
// NOTE: Tasks can also be created here!
// Start the RTOS scheduler.
vTaskStartScheduler();
}
協(xié)程函數(shù)也被擴(kuò)展,因此每個(gè)協(xié)程使用的 LED 和閃爍速度都不同。
const int iFlashRates[ NUM_COROUTINES ] = { 10, 20, 30, 40, 50, 60, 70, 80 };
const int iLEDToFlash[ NUM_COROUTINES ] = { 0, 1, 2, 3, 4, 5, 6, 7 }
void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
{
crSTART( xHandle );
for( ;; )
{
????????//?根據(jù)索引設(shè)定延時(shí)
crDELAY( xHandle, iFlashRate[ uxIndex ] );
// LED閃爍
vParTestToggleLED( iLEDToFlash[ uxIndex ] );
????}
crEND();
}
RTOS中協(xié)程與任務(wù)的區(qū)別
上面通過(guò)案例介紹的協(xié)程的內(nèi)容,有使用過(guò)RTOS(任務(wù))編程的朋友應(yīng)該都能看明白,其實(shí)任務(wù)和協(xié)程有很多相似之處,但也有很多區(qū)別:
1、調(diào)度和管理
任務(wù)由操作系統(tǒng)進(jìn)行調(diào)度和管理,而協(xié)程不需要系統(tǒng)調(diào)度器來(lái)管理,而是由用戶自己管理。
2、占用資源
任務(wù)通過(guò)系統(tǒng)調(diào)用,會(huì)占用更多系統(tǒng)資源,而協(xié)程在單個(gè)線程中執(zhí)行,不會(huì)像多線程那樣消耗大量的系統(tǒng)資源。
3、RAM使用量
協(xié)程不占用系統(tǒng)資源,因此協(xié)程更適合用于RAM較小的處理器,如早期的8位、16位MCU。
4、限制
協(xié)程的優(yōu)勢(shì)是使用的 RAM 較少,但代價(jià)是協(xié)程存在更多的限制。與任務(wù)相比,協(xié)程的限制性更強(qiáng),使用起來(lái)也更復(fù)雜 。
5、執(zhí)行效率
由于協(xié)程的切換是由程序自身控制的,沒(méi)有線程切換的開(kāi)銷(xiāo),因此協(xié)程在執(zhí)行效率上通常更高。
6、......
早期,MCU資源和性能相對(duì)都不高,有些RTOS(如FreeRTOS)都有協(xié)程的功能。但是,隨著MCU資源和性能的提升,協(xié)程已經(jīng)被線程(任務(wù))替代了。