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