Libfly协程库简介:
libfly协程库,采用C++作为编程语言,封装了协程类,并提供了协程基本操作和调度功能
编写此协程库的目的是为了让协程理解起来更容易,让协程编程更加简单!
正逐步完善代码注释与文档,欢迎大家一起讨论交流~
Github地址:https://github.com/chudongfang/libfly
一、协程的实现
与线程类似一个协程包括以下三个方面:
(1) 有一段程序供其执行,不同的协程可以共用同一段程序。
(2) 与线程一样,协程有专属的系统堆栈空间。
(3) 一个存储协程相关信息的协程控制块,这里我把其封装在一个协程类中。
协程类
先来看一下协程类,其描述了一个协程,包含了协程的所有信息。
//协程块
class Routine
{
public:
Routine(RoutineEnv * env,const RoutineAttr* attr ,void*(*pfn)(void*),void *arg );
~Routine();
void resume(); //运行当前协程
void yield(); //退出当前协程
public:
RoutineEnv * env_; //当前协程环境
Routine_Function pfn_; //协程块函数
void* arg_; //协程块函数对应的参数
coctx_t ctx_; //用来保存CPU上下文
char Start_; //协程是否运行
char End_; //协程是否结束
char IsMainRoutine_; //是否是主协程
StackMemry* stack_memry_; //协程运行栈内存
char* stack_sp_; //顶指针
unsigned int save_size_; //buff大小
char* save_buffer_; //buff
unsigned long long getId() const //获取协程ID
{ return Id_; }
void setId(unsigned long long Id) //设置协程ID
{ Id_ = Id; }
private:
unsigned long long Id_; //协程ID
};
栈内存 stack_memry_
其中 stack_memry_ 为其栈内存,记录协程运行过程中的信息。
//协程栈
class StackMemry
{
public:
StackMemry(int stack_size);
public:
Routine* occupy_co_; //当前协程指针
int stack_size_; //栈大小
char* stack_bp_; //stack_buffer + stack_size
char* stack_buffer_; //栈空间
};
协程的创建
好,看完协程快的结构,下面来看一下如何创建一个协程
//构造函数原型
//env 当前线程enb
//attr 协程栈参数
//void*(*Func)(void*) 协程函数
//arg 协程函数参数
Routine(RoutineEnv * env,const RoutineAttr* attr ,void*(*Func)(void*),void *arg );
// for example
void* Func(void *arg)
{
int* x = (int*)arg;
std::cout<<*x<<std::endl;
}
int x = 1;
Routine * routine = new Routine(get_curr_thread_env(),NULL,Func,&x);
其中的Func就是协程函数,其为将要在协程中执行的函数。由用户提供。
二、基本操作的实现
协程 resume 唤醒/执行
前面已经创建了一个协程,那么如何唤醒它那?
//routine->resume();
void* Func(void *arg)
{
int* x = (int*)arg;
std::cout<<*x<<std::endl;
}
int x = 1;
Routine * routine = new Routine(get_curr_thread_env(),NULL,Func,&x);
routine->resume();
resume 内部实现:
//执行本协程
void Routine::resume()
{
RoutineEnv *env = env_;
Routine *Current_Routine = env->CallStack_[ env->CallStackSize_ - 1 ]; //取当前协程块指针
//std::cout<<env->CallStackSize_<<std::endl;
//协程首次执行,创建其上下文
if( !Start_ )
{
coctx_make( &ctx_,(coctx_pfn_t)RoutineFunc,this,0 );
Start_ = 1;
}
env->CallStack_[ env->CallStackSize_++ ] = this;//将新协程块指针压入pCallStack栈中
co_swap( Current_Routine, this );
}
其就是把当前协程的寄存器信息和指针信息与入寄存器,co_swap coctx_make 都是用来复制寄存器信息的,这个放到后面解释。RoutineEnv是存储当前进程中协程信息的这个放到后面会有专门的解释。
协程 yield 退出
//routine->yiled();
void* Func(void *arg)
{
int* x = (int*)arg;
std::cout<<*x<<std::endl;
}
int x = 1;
Routine * routine = new Routine(get_curr_thread_env(),NULL,Func,&x);
routine->resume();
routine->yiled();
yiled 实现原理
//退出本协程
void Routine::yield()
{
//确保不重复退出协程
assert(End_ == 0 && Start_ == 1);
yield_env(env_);
}
void yield_env(RoutineEnv* env_)
{
Routine *Last_routine = env_->CallStack_[ env_->CallStackSize_ - 2 ]; //获取上一个协程块
Routine *Current_Routine = env_->CallStack_[ env_->CallStackSize_ - 1 ]; //获取当前协程块
env_->CallStackSize_--;
co_swap( Current_Routine, Last_routine);
}
其实也很简单,获取上一个协程块后,退出当前协程,把上一个协程信息写入寄存器就OK了。
在协程类离开作用域或者被delete后,会自动调用析构函数,释放栈空间。
协程的基本简单实现就到这里了,接下来还会有 协程寄存器切换 ,定时器,事件驱动EventLoop ,
RoutineEnv等内容~