我是靠谱客的博主 高挑红牛,最近开发中收集的这篇文章主要介绍c++协程库实现-day2指路:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

代码部分

  • 指路:
    • xfiber.cpp
    • xfiber.h

指路:

B站:毛毛and西西 视频 : c++携程库,第2部分
协程框架已开源到github和gitee上
gitee地址:https://gitee.com/meiqizhang/xfiber
github地址:https://github.com/meiqizhang/xfiber.git

xfiber.cpp

#include "xfiber.h" //先扫描当前路径下寻找,然后再到系统库下面
#include <iostream>
XFiber::XFiber()
{
    //XFIBER调度器新建时,没有任何fiber类被调度
    this->cur_dispatch_fib_ = nullptr;//当前被调度的fiber对象(协程))
    this->ready_fibers_ = std::list<Fiber*>();//两个协程运行队列
    this->run_fibers_ = std:: list<Fiber*>();
    //XFIBER上下文信息爆出只能在这里
    //this->XFiber_ctx_ = 
}
XFiber::~XFiber()
{
}
void XFiber::CreateFiber(std::function<void()> run)
{
    Fiber * fiber = new Fiber(run,this);
    this->ready_fibers_.push_back(fiber);
    return ;
}
//获取 XFIBER调度器的当前上下文
ucontext_t * XFiber::Get_X_FiberCtx()
{
    return &this->XFiber_ctx_;
}
//XFIBER调度器的调度逻辑
void XFiber::Dispatch()
{
    //一个死循环,一直调用
    while(true) {
        if (this->ready_fibers_.empty()) {
            //如果就绪队列为空, 就没有协程可以调度
            //在这里等着吧
            continue;
        } 
        //就绪队列不为空
        //std::move :利用拷贝构造
        //知识点: 完美转发, 万能引用 + 应用折叠
        this->run_fibers_ = std::move(this->ready_fibers_);
        this->ready_fibers_.clear();//记得要清理
        
        //现在需要对运行队列里面的协程进行调度
        //先来先服务
        for (auto iter = this->run_fibers_.begin();iter != this->run_fibers_.end();iter ++) {
            //遍历 运行队列,运行队列里面的每一个FIBER对象都是XFIBER调度器的调度对象
            //获取下一个需要调度的对象
            Fiber * fiber = * iter;
            // 该对象即将要上 xfiber调度器了,
            //提前保存该对象,考虑到调度途中 万一需要让出cpu(暨YILED)
            this->cur_dispatch_fib_ = fiber;
            //XFIBER调度器调度Fiber对象(暨协程)
                //如何调度? swapcontext来进行CPU上下文切换
            //解释:
            /*
                1.当前cpu上运行的是XFIBER调度类,
                    ==> 所以当前cpu上下文也是 XFIBER的上下文
                2.XFIBER即将调度fiber对象上cpu运行,
                    ==> 所以就需要保存 当前 XFIBER得 上下文信息
                    ==> 并且 使用 cur_distance_fiber 的上下文信息来进行运行
                3. 由于 cur_dispacth_fib_.fib_ctx.uc_link == xfiber.xfiber_ctx
                    ==> 所以 在 FIBER对象(被调度协程)运行完毕后,会回归到 xfiber(调度协程)的上下文
                    ==> 所以 ,会回之后 的下一条命令 就是 cur_dispatch_fib_ = nullptr
            */
            swapcontext(this->Get_X_FiberCtx(),cur_dispatch_fib_->Get_FiberCtx());
            //该调度对象(暨协程)已经彻底啊调度完成
                // ===> 为什么说调度完成了?
                //      因为他已经 从被调度的fiber对象的上下文中,再一次的返回到了 XFIBER的上下文
                //      浏览 FIBER中 ctx的初始化
                //      fiber->fiber_ctx_.uc_link = xfiber_ctx_
                //      任何一个 fiber对象(暨协程),在调度后,都必须回到XFIBER调度器,从而继续调度运行队列里面的后续协程
            // 该fiber对象调度完成,就就没啥用了,所以就不需要保存了
            this->cur_dispatch_fib_ = nullptr;
            if (fiber->IsFinished()) {
                //如果 被调用的fiber对象(暨被调用协程)已经运行完毕,则删除掉这个fiber
                delete fiber;
            }
            
        }
        //运行队列中所有的被调度FIBER(暨被调度协程)已经调度完成
        this->run_fibers_.clear();
    }
    return ;
}
//调用该函数的协程: 主动让出cpu
// 让出cpu的结果就是,会回到XFIBER调度器,有调度器,去选择下一个调度对象
void XFiber::Yiled()
{
    //主动调用 YILED函数是,说明 当前被调度的fiber类一定没有 运行完毕
    //所以 保存的cur_dispatch_fib_ 还不是null
    // 把没有运行完毕的 fiber对象,放入 就绪队列里面去
    this->ready_fibers_.push_back(this->cur_dispatch_fib_);
    //现在 被调用fiber对象(暨被调用的协程),需要主动返回到XFIBER调度器(暨:xfiber协程)
    //  解释:
    /*
        1. 由于当前 fiber并没有彻底完成 isfinished is no 
            ==> 所以需要保存,当前fiber的当前上下文信息
        2. 由于让出cpu之后,需要 让XFIBER继续调度
            ==> 所以需要返回到 xFIBER协程
                ==> 暨,切换到 XFIBER调度类的上下文信息
                    ==> XFIBER,在第一次进入高fiber调度类时, 上下文信息是保存在XFIBER.xifber_ctx_中的
                        所以我们在切换回来就可以了
        
    */
    swapcontext(cur_dispatch_fib_->Get_FiberCtx(),this->Get_X_FiberCtx());
    return ;
}
//功能: 执行某一个 fiber的入口函数
//执行完毕,就需要改变状态
//该函数只是 一层包装,
// 调用该函数,在该函数内部 进行真正的 入口函数调用
void Fiber::Fiber_Entryfunc_Start(Fiber * fiber)
{
    // 每一个fiber对象,第一次被调度时
    //都会进入该函数
    // ===> 因为 在swapcontext(xfiber.ctx,fiber.ctx)前
    //  ==> 都早早的  makecontext(fiber.ctx,func,1,fiber)了
    fiber->entry_func_();
    //入口函数调用完成,说明fiber调度完成
    fiber->status_ = -1;
    return ;
}
Fiber::Fiber(std::function<void()> entry_func_,XFiber * xfiber)
{
    //fiber对象(暨背调度的协程)的入口函数地址
    //  该入口函数地址是保存在 uc_mcontext中的:
    // uc_mcontext:保存所有上下文信息,寄存器信息,入口函数地址

    this->entry_func_ = entry_func_;
    // isFinished的判断条件
    this->status_ = 0;
    getcontext(&this->Fiber_ctx_);
    //有栈协程: 栈大小为 128kb
    this->stack_size_ = 1024 * 128;
    this->Fiber_ctx_.uc_stack.ss_size = this->stack_size_;
    //协程的栈起始位置
    this->stack_sp_ = new char[this->stack_size_];
    this->Fiber_ctx_.uc_stack.ss_sp = this->stack_sp_;
    //最最最最最最最最最最最最重要的一点
    // =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
    //      任何一个 fiber对象的 fiber_ctx_.uc_link 一定要是
    //          xfiber.xfiber_ctx
    //          一定要保成,每一个被调用协程的后续上下文是
    //          xfiber调度器的上下文
    //          这样 才能保证 每一次调用完毕,都会回到
    //          xfiber
    // =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
    this->Fiber_ctx_.uc_link = xfiber->Get_X_FiberCtx();

    //需要注意,参数个数
    //makecontext(&this->Fiber_ctx_,(void(*)())Fiber::Fiber_Entryfunc_Start,1,this);
    makecontext(&this->Fiber_ctx_,(void(*)())Fiber::Fiber_Entryfunc_Start,1,this);
    return ;
}
Fiber:: ~Fiber()
{
    delete this->stack_sp_;
    stack_sp_ = nullptr;
    stack_size_ = 0;
}


bool Fiber::IsFinished()
{
    return status_ == -1;
}
ucontext_t * Fiber::Get_FiberCtx()
{
    return &Fiber_ctx_;
}

xfiber.h

#pragma once
#include <functional>
#include <list>
#include <ucontext.h>
#include <iostream>
class Fiber;
class XFiber {
    public:
        XFiber();
        ~XFiber();
        //Xfiber调度类,新建一个 fiber对象
        // 每一个fiber对象都是一个协程
        // run就是协程的 入口函数
        void CreateFiber(std::function<void()> run);
        //Xfiber调度类,通过该函数,
        //对 Fiber对象进行调度
        void Dispatch();
        //XFiber调度类,通过使用该函数
        //来是 正在调度的 Fiber对象,让出CPU
        //然后 回到 XFiber的调度上下文,继续进行调度
        void Yiled();
        //获取当前XFIBER调度类的调度上下文
        ucontext_t * Get_X_FiberCtx();
    private:
        //XFIBER调度器 正在调度的fiber对象(一个协程)
        Fiber * cur_dispatch_fib_;
        //XFIBER的调度上下文:
        //Xfiber也是一个协程,
        //在调度完成任何一个fiber对象后,都应该继续执行该XFIBER协程
        ucontext_t  XFiber_ctx_; 
        // 两个队列: 模仿 CPU调度
        std::list<Fiber *> ready_fibers_; //就绪队列 
        std::list<Fiber *> run_fibers_;  // 运行队列 
};
class Fiber {
    public:
        //构造函数: 参数1: 协程的入口函数,参数2:当前的XFIBER调度器
        Fiber( std::function<void()> run,XFiber * xfiber);
        ~Fiber();
        
        //为什么这个是静态函数?
        // 参数有什么意义?
        //解答: 1.参数,FIBER对象
        //      2.如果是成员函数,那么 makecontext时 并不是很好绑定(如果刚开始类没有实例化时,start函数为null)
        // 这样并不是很好,静态函数,可以保证任何时候这个函数都是存在的
        static void Fiber_Entryfunc_Start(Fiber * fiber);
        //获取当前FIBER对象的上下文信息
        ucontext_t * Get_FiberCtx();
        //判断 当前FIBER是否调度完成
        bool IsFinished();
    private:

        int status_;
        char * stack_sp_;
        size_t stack_size_;
        //该协程的入口函数 (makecontext会绑定协程的入口函数地址并且,进行上下文切换)
        std::function<void()> entry_func_;
        ucontext_t Fiber_ctx_;// 保存当前上下文信息
        // uc_link : ucp结构可以形成一个链表
        // uc_flage:
        // uc_stack: ucp结构堆栈信息:
            //uc_stack.ss_sp :栈顶指针
            //uc_stack.ss_size : 栈空间大小
            //uc_stack.ss_flage :
            //协程分类,有栈协程和无栈协程
        //uc_mcontext :存储当前上下文 ===>各种各样的寄存器信息
            //注意: get/set_context修改的都是 这里面的
            //      makecontext: 将后续上下文入口函数地址,也是保存在 这些寄存器信息里面的
        //uc_sigmask:喜好屏蔽掩码
};

最后

以上就是高挑红牛为你收集整理的c++协程库实现-day2指路:的全部内容,希望文章能够帮你解决c++协程库实现-day2指路:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部