C 语言实现协程,最困难的部分就是上下文信息的保存和还原。这样才能够做到,让协程在任意位置让出执行权限,稍后再恢复到中断位置继续执行。C 实现协程一般有几个方案。
使用第三方库来保存恢复上下文数据,比如ucontext
使用汇编来保存上下文信息
使用setjmp / longjmp 保存恢复上下文信息
使用switch case的特性来做上下文断点继续,上下文信息需要用static变量保存。比如Protothreads
使用线程来保存上下文信息
本文,使用了switch case的特性来保存中断位置,使用数据结构和static变量来保存上下文信息,使用宏来构建API调用。由于我使用过lua和unity c#协程进行了产品开发。所以,这套实现会贴近unity中C#的使用习惯。完成了一下功能:
在协程执行的任意位置暂停,让出执行权限
恢复协程继续上次中断的地方继续执行
通过static变量和数据结构保存协程数据
协程让出执行后,等待特定的帧数,时间,和其它协程完成
开始看代码:
typedef enum
{
/**
* Coroutine wait for frame count to waitValue
*/
coroutine_wait_frame,
/**
* Coroutine wait for second count to waitValue
*/
coroutine_wait_second,
/**
* Coroutine wait for other Coroutine to finish
*/
coroutine_wait_coroutine,
/**
* Coroutine just run forward
*/
coroutine_wait_none,
}
CoroutineWaitType;
先定义协程让出执行后,等待的类型。可以看到这里定义了几种类型,可以等待帧数,时间,其它协程。
typedef enum
{
/**
* Coroutine enter queue ready to running
*/
coroutine_state_ready,
/**
* Coroutine has started to execute
*/
coroutine_state_running,
/**
* Coroutine already finished and waiting for reuse
*/
coroutine_state_finish,
}
CoroutineState;
这里定义协程的状态。等待执行,正在执行包括中断的也算在执行的,还有执行完成的。我们后面会介绍,有一个协程管理器。所有的协程进入管理器,被轮询检测。完成后的协程会被缓存起来,下次请求协程的时候会先检查缓存的协程可否使用。
typedef struct Coroutine Coroutine;
typedef void (*CoroutineRun)(Coroutine* coroutine);
struct Coroutine
{
/**
* Record coroutine run step