这是一个轻量级的协程调度框架,支持协程的创建、调度、销毁、挂起、恢复、等待、超时等操作。它基于Protothreads实现,扩展出了更高效的使用接口。
- 支持创建和销毁协程
- 支持协程延时
- 支持异步执行
- 极低的内存使用
- 非抢占式调度
- 静态内存管理
- 支持低功耗模式
- 创建一个协程,每隔1500ms输出"hello world!":
PT_THREAD(test_task(struct pt *pt)){
PT_BEGIN(pt);
while(1){
// Output "hello world!" every 1500 ms.
PT_TASK_DELAY(pt, 1500);
printf("hello world!\r\n");
}
PT_END(pt);
}
int main(void){
PT_TASK_RUN(test_task);
//...
for(;;){
PT_TASK_SCHEDULE();
}
return 0;
}
2.创建一个协程,每隔1秒输出计数器值,输出到10后删除协程:
PT_THREAD(test_task(struct pt *pt)){
PT_BEGIN(pt);
// Only static variables can be used in PT_THREAD.
static int i = 0;
while(1){
PT_TASK_DELAY(pt, 1000);
i++;
printf("%d\r\n", i);
if(i == 10){
// It will be automatically deleted after exit.
PT_EXIT(pt);
}
}
PT_END(pt);
}
int main(void){
PT_TASK_RUN(test_task);
while(PT_TASK_NUMS() > 0){
PT_TASK_SCHEDULE();
}
return 0;
}
3.创建两个协程,协程A输出一个信号量,协程B等待信号量,输出"Obtained semaphore!": *** 注意:非超时的等待信号量不可用在低功耗模式下 ***
struct pt_sem sem;
PT_THREAD(test_task_A(struct pt *pt)){
PT_BEGIN(pt);
while(1){
PT_TASK_DELAY(pt, 1000);
PT_SEM_SIGNAL(pt, &sem);
}
PT_END(pt);
}
PT_THREAD(test_task_B(struct pt *pt)){
PT_BEGIN(pt);
while(1){
PT_SEM_WAIT(pt, &sem);
printf("Obtained semaphore!\r\n");
}
PT_END(pt);
}
int main(void){
PT_SEM_INIT(&sem, 0);
PT_TASK_RUN(test_task_A);
PT_TASK_RUN(test_task_B);
for(;;){
PT_TASK_SCHEDULE();
}
return 0;
}
4.创建一个协程,协程内同时执行两个异步动作:
PT_THREAD_DECL(invok_test, {
static int cnt;
cnt = 0;
PT_INVOK({
static int i;
for(i = 0; i < 10; i++){
printf("async invok %d\r\n", i);
cnt += i;
PT_TASK_DELAY(pt, 1000);
}
// Automatically exit this asynchrony at end of PT_INVOK();
});
PT_INVOK({
while(cnt < 5){
printf("hello invok, cnt = %d\r\n", cnt);
PT_TASK_DELAY(pt, 300);
}
});
});
int main(void){
PT_TASK_RUN(invok_test);
while(PT_TASK_NUMS() > 0){
PT_TASK_SCHEDULE();
}
return 0;
}
5.可以超时的等待信号量(低功耗模式)
struct pt_sem sem;
PT_THREAD(test_task_A(struct pt *pt)){
PT_BEGIN(pt);
static int i;
for(i = 0; i < 10; i++){
PT_TASK_DELAY(pt, 1000);
PT_SEM_SIGNAL(pt, &sem);
}
PT_END(pt);
}
PT_THREAD(test_task_B(struct pt *pt)){
PT_BEGIN(pt);
static int err_cnt = 0;
while(1){
int ret;
PT_SEM_WAIT_TIMEOUT(pt, &sem, 2000, &ret);
if(ret == 0){
printf("Obtained semaphore! - %d\r\n", clock_time());
}else{
printf("Obtained semaphore timeout - %d\r\n", clock_time());
err_cnt++;
if(err_cnt >= 3){
PT_EXIT(pt);
}
}
}
PT_END(pt);
}
int main(void){
PT_SEM_INIT(&sem, 0);
PT_TASK_RUN(test_task_A);
PT_TASK_RUN(test_task_B);
while(PT_TASK_NUMS() > 0){
PT_TASK_SCHEDULE();
clock_time_t idle_time = PT_TASK_IDLE_TIME();
if(idle_time > 0){
// Calling system delay functions
sleep(idle_time);
}
}
return 0;
}
详细的文档可在pt_plus.h的注释中找到。
pt plus的接口基本采用宏实现,可兼容大部分编译器,最好使用C99以上的标准编译器。
移植到其它运行平台,需要实现clock_arch.c文件中的clock_time()函数,它返回当前系统的时刻,这将作为协程调度的时间基准。当然这不是必要的,当PT_PLUS_DELAY_SUPPORT=0时,可以关闭所有与时间相关的接口。
该框架是基于非抢占式模式进行设计的,如果在中断中使用信号量或任务创建,需要自行做保护处理,所有的接口中断安全的无法保证。
在大部分情况下,协程内禁止使用局部变量,所有的协程函数体中变量必须声明为静态的。除非该变量的生命周期极短,且它的生命周期内没有进行协程调度。
对于低功耗模式来说,使用存在一些限制。当使用PT_WAIT_UNTIL()、PT_WAIT_WHILE()、PT_SEM_WAIT()接口时,协程将进入无超时的等待状态,此时系统无法评估全局的低功耗等待时间,因此,当使用这些接口时,将不能使用PT_TASK_IDLE_TIME()去评估休眠的时长。
函数名 | 功能 |
---|---|
PT_TASK_SCHEDULE() | 协程调度 |
PT_TASK_IDLE_TIME() | 获取协程调度可休眠的时长 |
PT_TASK_NUMS() | 获取当前协程数量 |
PT_TASK_RUN(func) | 创建一个协程 |
PT_THREAD_DECL(name, body) | 定义协程函数体 |
PT_TASK_DELAY(pt, ms) | 延时函数 |
PT_INVOK(body) | 协程异步执行 |
PT_YIELD(pt) | 协程主动让出执行权 |
PT_EXIT(pt) | 协程主动退出 |
PT_WAIT_UNTIL(pt, condition) | 等待条件成立 |
PT_WAIT_WHILE(pt, cond) | 当条件成立时等待 |
clock_time() | 获取当前系统时钟 |
PT_SEM_INIT(sem, count) | 初始化信号量 |
PT_SEM_WAIT(pt, sem) | 等待信号量 |
PT_SEM_WAIT_TIMEOUT(pt, sem, timeout, ret) | 可超时的等待信号量 |
PT_SEM_SIGNAL(pt, sem) | 发出一个信号量 |
make test