Libfly协程库实现与原理——协程的实现与基本操作的实现


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等内容~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值