概述
代码部分
- 指路:
- 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指路:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复