我是靠谱客的博主 高挑红牛,这篇文章主要介绍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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#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指路内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部