大家好,欢迎来到IT知识分享网。
1.什么是协程
正如网络上大家都在讲的“用户态的线程,更轻量级”,
2.协程跟进程,线程区别
2.1进程
1.程序的一次执行过程;
2.系统资源分配的单位,具有独立的虚拟内存,PCB控制块,代码,数据,堆栈,文件资源等;
3.进程上下文切换开销大(主要问题)
2.2线程
1.系统调度的单位;
2.共享进程的资源空间;具有自己的栈,寄存器,
3.并发性好,同一进程内的线程切换不会导致进程的上下文切换,所以线程切换开销小;
4.共享进程资源,多线程会发生冲突,需要进行同步。
2.3协程
1.可以理解为线程中的线程;
2.具有自己的上下文;
3.协程切换不会引起线程的上下文(更轻量)
4.一个线程同一时刻只有一个协程执行;
5.协程执行任意时刻都可以退出,下次可以继续执行(与函数的区别,函数一条路走到黑)
6.高并发场景下性能很好,并且不需要同步
3.为什么要产生协程
其实协程效率高并不是根本原因;
在高并发场景下
两个原因:
1.用同步代码逻辑简单,并发性差强人意
同步阻塞:阻塞等待业务处理,期间不能做其他事情
同步非阻塞:不断轮询,忙等待占用cpu,
2.用异步,并发性好,代码逻辑又很复杂
基于以上就产生了协程,协程可以做到用同步的代码逻辑达到异步的效果
4.协程的优缺点
优点:
1.用同步的思想编写出异步的效果,
2.并发性好,性能高
缺点:
单线程中同时只能执行一个协程,无法利用多核功能,还是得结合多线程
5.常见的协程库
Boost.Coroutine2:这是Boost库中的⼀个模块,提供了⼀组灵活的协程实现,包括对称协程和⾮对称协程。它提供了⼀些强⼤的特性,如⽀持多个栈和⾃定义栈⼤⼩。
libco:腾讯微信团队开源的⼀个C/C++协程库,据说微信后台⼤量在使⽤这个库,通过⼏个简单的接⼝就能实现协程的创建/调度,同时基于epoll/kqueue实现了⼀个事件反应堆,再加上sys_call(系统调⽤)hook技术,以此给开发者提供同步/异步的开发⽅法
libgo :是⼀个使⽤ C++ 编写的协作式调度的stackful有栈协程库, 同时也是⼀个强⼤的并⾏编程库。⽀持linux平台,MacOS和windows平台,在c++11以上的环境中都能⽤。
6.ucontext库的使用
6.1协程上下文ucontext结构体
typedef struct ucontext_t { struct ucontext_t *uc_link; //该上下文指向的下一个上下文 stack_t uc_stack; //栈的信息,是一个stack_t结构体 mcontext_t uc_mcontext; //保存了上下文各种寄存器信息 sigset_t uc_sigmask; //当上下文被激活时屏蔽的信号集 } ucontext_t;
6.2栈信息结构体
typedef struct { void *ss_sp;//栈空间指针,指向栈空间的地址 int ss_flags;//栈的flags size_t ss_size;//栈的大小 } stack_t;
6.3ucontext还提供了四个API函数
#include<ucontext.h> //获取协程上下文 int getcontext(ucontext_t *ucp); //设置协程上下文 int setcontext(const ucontext_t *ucp); /*设置协程入口函数,使用前需要配合getcontext()使用, 如果指定了ucontext中的uc_link,那么函数执行完会返回到uc_link执向的上下文*/ void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...); //保存当前上下文到oucp,前往ucp执向的上下文,相当于切换上下文 int swapcontext(ucontext_t *oucp, ucontext_t *ucp);
说这么多不如上案例,
1.先来试试getcontext和setcontext的用法
#include <ucontext.h> #include <iostream> void func(int a) { std::cout << "func begin" << std::endl; std::cout << a++ << std::endl; std::cout << "func end" << std::endl; } int main() { ucontext m_ctx, c_ctx;//定义两个上下文 getcontext(&m_ctx);//初始化m_ctx,此时记录执行位置为下一行 func(10);//调用func setcontext(&m_ctx);/*设置上下文m_ctx,m_ctx的执行位置是调用func的那一行,又回到刚才的位置 所以程序无线循环*/ return 0; }
可以看到程序无线循环打印a的值,不会退出
2. 我们再试试makecontext的用法
#include <ucontext.h> #include <iostream> #include <stdlib.h> int a = 10; void func() { std::cout << "func begin" << std::endl; std::cout << a++ << std::endl; std::cout << "func end" << std::endl; } int main() { ucontext m_ctx, c_ctx; getcontext(&m_ctx); // 初始化上下文 m_ctx.uc_link = nullptr; // uc_link指向执行完入口函数返回的位置,为nullptr则退出, m_ctx.uc_stack.ss_sp = malloc(128 * 1024); // m_ctx.uc_stack.ss_size = 128 * 1024; makecontext(&m_ctx, func, 0); // 将上下文绑定入口函数 setcontext(&m_ctx); // 执行该上下文 return 0; }
可以看到我们执行完入口函数就结束了
3.再来试试swapcontext
#include <ucontext.h> #include <iostream> #include <stdlib.h> int a = 10; ucontext m_ctx, c_ctx; void func() { std::cout << "func begin" << std::endl; swapcontext(&m_ctx, &c_ctx); // 保存当前上下文,切换到ctx上下文 std::cout << a++ << std::endl; std::cout << "func end" << std::endl; } int main() { getcontext(&m_ctx); // 初始化上下文 m_ctx.uc_link = nullptr; // uc_link指向执行完入口函数返回的位置,为nullptr则退出, m_ctx.uc_stack.ss_sp = malloc(128 * 1024); // m_ctx.uc_stack.ss_size = 128 * 1024; makecontext(&m_ctx, func, 0); // 将上下文绑定入口函数 swapcontext(&c_ctx, &m_ctx); // 将当前上下文保存到c_ctx, 执行m_ctx上下文 return 0; }
可以看到函数执行中途就切换回来了,那么能不能再切换回去呢,答案是肯定的
4.我们在主函数再加一个swap再切换回去
#include <ucontext.h> #include <iostream> #include <stdlib.h> int a = 10; ucontext m_ctx, c_ctx; void func() { std::cout << "func begin" << std::endl; swapcontext(&m_ctx, &c_ctx); // 保存当前上下文,切换到ctx上下文 std::cout << a++ << std::endl; std::cout << "func end" << std::endl; } int main() { getcontext(&m_ctx); // 初始化上下文 m_ctx.uc_link = nullptr; // uc_link指向执行完入口函数返回的位置,为nullptr则退出, m_ctx.uc_stack.ss_sp = malloc(128 * 1024); // m_ctx.uc_stack.ss_size = 128 * 1024; makecontext(&m_ctx, func, 0); // 将上下文绑定入口函数 swapcontext(&c_ctx, &m_ctx); // 将当前上下文保存到c_ctx, 执行m_ctx上下文 swapcontext(&c_ctx, &m_ctx); return 0; }
可以看到又切换会回函数内部继续执行了,代码这里没有加区分的语句,加了的话就更明显了
5.由此我们是不是可以联想到协程可以做很多事情,比如执行某个系统调用阻塞了,我们可以暂时切换到别的协程去执行别的操作,等一段时间再来执行原来的协程。这样效率是不是更高了。
6.协程相关概念
6.1非对称协程跟对称协程
非对称:每个子协程都由主协程调度执行,退出也要返回到主协程,主协程再调度其他的子协程
主协程:每个协程在自己内部可以调度其他协程,不需要主协程充当媒介来调度。
6.2有栈协程跟无栈协程
有栈协程:有自己的栈寄存器,可以绑定入口函数执行
无栈协程:一般只用来调度,如主协程
6.3共享栈协程
1.多个协程共享一个栈,可以节约空间
2.一般我们都是独享栈,比较浪费空间资源,但是也便于操作
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/116964.html




