快速上手,协程剖析

coroutine.pdf

协程也叫微线程,英文名称为coroutine。一个进程可以有多个线程,一个线程可以有多个协程,这是协程和线程间的关系。不同的是,线程由系统调度,但协程需要自己调度,协程运行在用户态。

Linux内核为协程编程提供了支持,相关的函数声明在ucontext.h头文件中。也可以借助longjmp、setjmp、pthread_attr_setstackaddr等组合实现,但复杂很多,ucontext提供的函数已帮助做了很多工作。要实现协程的并发(线程内的,显然是假并发,实际还是串行的),要求主动调用swapcontext进行切换。

协程编程实际就是用户态调度函数的执行次数,让单个线程,看起来像是多线程。基于它可以实现伪同步,也就是将异步变成同步调用。

协程的原理非常简单,假设任务A划分成A1、A2、A3三个子任务,任务B划分成任务B1和B2两个子任务。利用协程,让A和B可以并行进行,比如完成A1后,立即执行B1,B1完成后执行A2,A2完成后执行B2,B2完成后执行A3。

为达到这个目的,在执行A1时,A1结束前需要调用swapcontext切换到B1。同理B1完成时,也需要调用swapcontext切换到A2。下面这个示例可以直接编译执行,通过它可以体会到协程的效果。

 		// 协程示例 
 		// 编译: g++ -g -o x x.cpp 	
 		#include  
 		#include  // 协程相关api所在头文件 
 		#include  
 		// 定义3个协程,类似于3个线程 
 		static void foo(); 	
 		static void woo(); 	
 		static void zoo(); 	
 		static ucontext_t ctx1; // 协程zoo的上下文,由makecontext调用构造 
 		static ucontext_t ctx2; // 协程woo的上下文,由makecontext调用构造 
 		static ucontext_t ctx3; // 协程foo的上下文,由swapcontext自动构造 
 		// stack为new/malloc出来的也可以的 
 		static char stack1[4096]; // 协程zoo的栈,得合适大小,否则一样会出现栈溢出 	
 		static char stack2[8192]; // 协程woo的栈,得合适大小,否则一样会出现栈溢出 	
 		int main() 	
 		{ 	
 		printf("main 1\n"); 	
 		    // 构造协程zoo的上下文 
 		getcontext(&ctx1); 	
 		ctx1.uc_stack.ss_sp = stack1; 	
 		ctx1.uc_stack.ss_size = sizeof(stack1); 	
 		ctx1.uc_link = &ctx3; // 在ctx1之后的上下文 
 		makecontext(&ctx1, zoo, 0); // 在运行完zoo之后,运行ctx3 
 		    // 构造协程woo的上下文 
 		getcontext(&ctx2);	 	
 		ctx2.uc_stack.ss_sp = stack2; 	
 		ctx2.uc_stack.ss_size = sizeof(stack2); 	
 		ctx2.uc_link = &ctx1; 	
 		makecontext(&ctx2, woo, 0); // 在运行完zoo之后,运行ctx1 
 		foo(); 	
 		printf("main 2\n"); 	
 		return 0; 	
 		} 	
 		// 可把foo当成一个线程,不过它是微线程 
 		void foo() 	
 		{ 	
 		printf("%s 1\n", __func__); 	
 		    // 切换到ctx2执行,也就是执行woo,当前的保存在ctx3 
 		swapcontext(&ctx3, &ctx2); 	
 		    // 当切回到ctx3时,会执行以下代码段 
 		printf("%s 2\n", __func__); 	
 		} 	
 		// 也可把woo当成一个线程,不过它是微线程 
 		void woo() 	
 		{ 	
 		printf("%s\n", __func__); 	
 		} 	
 		// 也可把zoo当成一个线程,不过它是微线程 
 		void zoo() 	
 		{ 	
 		printf("%s\n", __func__); 	
 		} 	

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小李刀刀的专栏

在WebKit中并行加载外部脚本译:

作者:Tony Gentilcore 原文:http://webkit.org/blog/1395/running-scripts-in-webkit/ Web...

35670
来自专栏前端vue

Node中间件multer文件上传实践

单文件上传,接收一个以fieldname命名的文件,文件信息保存在req.file

12720
来自专栏Vamei实验室

Linux常用命令

我总结了常用的Linux命令,方便你的Linux使用。下面是格式说明,你现在可以跳过,直到遇到疑问时再来查询。 $ 命令行提示符 粗体表示命令 斜体表示参数 ...

39670
来自专栏圣杰的专栏

VSTS 执行git pull报错问题修复

因此我们在执行git pull之前添加一个Command Line指令去执行git config即可,具体配置如下:

7220
来自专栏专注数据中心高性能网络技术研发

如何解压RPM包

Mellanox的驱动源码在centos7下面是使用RPM包封装的,需要解压此格式的包来获取源文件 RPM包括是使用cpio格式打包的,因此可以先转成cpio然...

89850
来自专栏数据分析

Windows PowerShell 学习之——Cmdlet处理生命周期

这一次介绍一下Cmdlet处理过程的生命周期 1. 概述 下图展示Windows PowerShell怎样处理一个管道请求指令。 这个流程包括: 指令参数(pa...

29960
来自专栏技术博文

linux最常用的20条命令

玩过Linux的人都会知道,Linux中的命令的确是非常多,但是玩过Linux的人也从来不会因为Linux的命令如此之多而烦恼,因为我们只需要掌握我们最常用的命...

35840
来自专栏别先生

mysql输入密码后闪退怎么办?

第一: 首先需要想到的是mysql的服务可能没开,首先打开mysql的服务 ? 第二: 打开Mysql的命令行输入密码即可 ? 第三: 登录成功 ? 第四: 顺...

25990
来自专栏性能与架构

Redis3 添加新节点到集群

向集群中添加新节点,有两种情况: (1)添加一个新节点,做为master,需要移动一部分slot到此节点 (2)添加一个新节点,做为slave,设置为集群中某个...

37070
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(十四) ——Redis RDB文件创建、载入与自动保存原理

《Redis设计与实现》读书笔记(十四) ——Redis RDB文件创建、载入与自动保存原理 (原创内容,转载请注明来源,谢谢) 一、概述 r...

35860

扫码关注云+社区

领取腾讯云代金券