c语言实现的协程
这几天突然对协程感兴趣,于是自己实现了一个,代码放在github上:https://github.com/adinosaur/Coro
协程是一种用户空间的非抢占式线程,主要用来解决等待大量的IO操作的问题。
协程vs线程
对比使用多线程来解决IO阻塞任务,使用协程的好处是不用加锁,访问共享的数据不用进行同步操作。这里需要说明的一点是,使用协程之所以不需要加锁不是因为所有的协程只在一个线程中运行,而是因为协程的非抢占式的特点。也就是说,使用协程的话,在没主动交出CPU之前都是不会被突然切换到其它协程上的。而线程是抢占式的,使用多线程你是不能确定你的线程什么时候被操作系统调度,什么时候被切换,因此需要用锁到实现一种“原子操作”的语义。
协程vs异步回调
其实更一般更常见的做法是,使用非阻塞的IO(比如是异步IO,又或者是在syscall上自己实现的一套异步IO,如asio)并且将处理操作写在回调函数中。这样的做法一般没什么问题,但当回调函数变多,一段连贯的业务代码就会被拆分到多个回调函数之中,增加维护的成本。因此使用协程可以用同步的写法写出效果相当于是异步的代码。
利用static变量实现协程
要实现一个协程,主要的问题是如何保存函数调用的上下文。之前在网上看到一篇博客coroutines in c,用一种非常简洁的方式实现了这个保存上下文的功能。实现代码如下:
#define crBegin static int _cr_state = 0; switch(_cr_state) { case 0:
#define crReturn(x) do { _cr_state = __LINE__; return x; case __LINE__:; } while (0)
#define crFinish }
int func1() {
crBegin
while ()
{
printf("hello world\n");
crReturn();
}
crFinish
}
这个代码利用了函数的static变量来保存函数调用状态。注意,由于vs2013有一个调试特性,所以vs2013的__LINE__的实现不是常量因此会编译不通过,使用gcc就可以编译。这段代码简单是简单但是有问题,比如说如果两个协程调用同一个函数,就会出错。因此博客里面提及这段代码主要是给出一个思路,如果实际使用的话这样子肯定是不行的。
利用setjmp、longjmp实现协程
前面说过,实现协程最主要的是保存函数的调用的上下文,而这些上下文主要就两个部分:1.各个寄存器的值,2.函数调用栈。C语言里可以通过setjmp来保存函数调用时,各寄存器的值。保存之后,便可以通过longjmp重现回到当初setjmp的地方(可以理解成跨函数的goto)。但是,需要注意的是,setjmp仅负责保存寄存器的值,不负责维护其函数调用栈(这个看看setjmp的jmp_buf的结构就知道了),因此必须由使用者来手动的维护这个函数调用栈。使用setjmp、longjmp的一个常见的错误就是,尝试去longjmp到一个已经执行完的函数,这时候虽然寄存器的值是当时保存的值,但是调用栈已经不是原来的调用栈了。
而我的做法是,在创建一个协程的时候在堆上申请一块空间(大小为2M)作为协程的调用栈,然后在setjmp的时候,手动更改寄存器esp的值,使其指向这个我自己创建的调用栈。因此在以后运行的时候,这个协程就会使用我提供的那块内存作为栈。
我的这个协程库提供了三个接口:
- coro_new:创建一个协程
- coro_yield:将控制权返回给调度协程
- coro_main:运行调度协程
协程的控制流程如下:
- 通过coro_main运行调度协程,并找出下一个运行的协程,运行之。
- 运行这个协程直到其调用coro_yield将控制权返还给调度协程。
- 重复以上两个步骤,直到所有协程运行完毕。
这个协程库实现的非常简单,只有100来行的代码,当然实现它的目的是为了提供一个最简单的协程模型,而不是一个功能完整、鲁棒性强的能投入实际业务运行的协程。
因此问题还是有很多的:
- 比如当在协程里面调用栈超过2M时,这个是需要处理的,现在的代码是没有做的,理应中断程序,避免写坏堆,产生随机的不可重现的问题。
- 显然在实现时没有考虑到多线程,如果在多线程环境里面运行,需要代码做同步处理。
- 现在的这个版本的协程有一个约定,在协程里调用的函数不能阻塞在syscall,这显然也是不科学的。一个完整的协程库,应该包含一些常用的syscall的非阻塞的实现,毕竟只有一个线程不能真的阻塞在这个调用上。
- 不同平台的jmp_buf实现可能不一致,因此在其它平台上需要稍微改写代码。
总结
当然实现协程还有比较一些更好的方法,比如如果能用glibc的ucontext库就可以基于这个库来实现,而不用自己手动管理函数调用的上下文了,如云风实现的协程库。
参考资料
- Coroutines in C。地址:http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
- CERL++。地址:https://zhuanlan.zhihu.com/p/19945225?refer=impress-your-cat
- C 的 coroutine 库。地址:http://blog.codingnow.com/2012/07/c_coroutine.html
c语言实现的协程的更多相关文章
- 022_go语言中的协程
代码演示 package main import "fmt" func f(from string) { for i := 0; i < 3; i++ { fmt.Print ...
- Go语言协程
协程的特点 1.该任务的业务代码主动要求切换,即主动让出执行权限 2.发生了IO,导致执行阻塞(使用channel让协程阻塞) 与线程本质的不同 C#.java中我们执行多个线程,是通过时间片切换来进 ...
- Go语言 进程、线程、轻量级进程、协程和go中的Goroutine 那些事儿
原文:http://www.cnblogs.com/shenguanpu/archive/2013/05/05/3060616.html 电话面试被问到go的协程,曾经的军伟也问到过我协程.虽然用py ...
- 浅谈Go语言的Goroutine和协程
0x00.前言 前面写了一篇初识Go语言和大家一起学习了Go语言的巨大潜力.语言简史.杀手锏特性等,感兴趣的读者可以回顾一下. 今天来学习Go语言的Goroutine机制,这也可能是Go语言最为吸引人 ...
- spawn协程学习
对于IO密集型的程序,一般比较高效的做法是选择异步来实现,因为使用异步的方法更容易写出高效的程序.然而使用异步的话,经验较少的人往往会使自己的程序结构变得很混乱,进而导致程序的可读性变差.记得有人说过 ...
- 进程、线程、轻量级进程、协程与 go 的 goroutine【转载+整理】
本文内容 进程 线程 协程 Go 中的 goroutine 参考资料 最近,看一些文章,提到"协程"的概念,心想,进程,线程,协程,前两个很容易,任何一本关于操作系统的书都有说,开 ...
- (zt)Lua的多任务机制——协程(coroutine)
原帖:http://blog.csdn.net/soloist/article/details/329381 并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上 ...
- Lua的协程(coroutine)
-------------------------------------------------------------------------------- -- 不携带参数 ---------- ...
- coroutine协程
如果你接触过lua这种小巧的脚本语言,你就会经常接触到一个叫做协程的神奇概念.大多数脚本语言都有对协程不同程度的支持.但是大多编译语言,如C/C++,根本就不知道这样的东西存在.当然也很多人研究如何在 ...
随机推荐
- linux运维自动化shell脚本小工具
linux运维shell 脚本小工具,如要分享此文章,请注明文章出处,以下脚本仅供参考,若放置在服务器上出错,后果请自负 1.检测cpu剩余百分比 #!/bin/bash #Inspect CPU # ...
- GUI生成exe文件
gui如何生成exe文件: 已经有gui.m和gui.fig文件 1 安装编译器.已经安装好了vs10的. 2 设置编译器.在matlab命令行输入mex -setup,选择安装的c编译器 3 调用编 ...
- 加密connectionStrings
开始菜单>>所有程序>>Microsoft visual studio 2010 >> Visual Studio Tools >> Visual St ...
- python学习笔记之基础二(第二天)
1.编码转换介绍 unicode是最底层.最纯的,会根据终端的编码进行转化展示 一般硬盘存储或传输为utf-8(因为省空间.省带宽),读入内存中为unicode,二者如何转换 a = ' ...
- Attribute和自定义Property
property(属性) attribute(特性) property和attribute的同步 id href value class/className 旧IE的趣事 attribute作为DOM ...
- C++ 构造函数、析构函数、拷贝构造、赋值运算符
之所以要把它们放在一起,是因为在使用C/C++类语言的时候,很容易混淆这几个概念(对Java来说完全没有这样的问题,表示Javaor完全没有压力). 先建立一个测试类(包含.h和.cpp) //~ P ...
- 数据分析 - 斯特鲁普效应(Stroop effect)
数据分析 - 斯特鲁普效应(Stroop effect) Reinhard得到了一份斯特鲁普效应试验的数据,我们来分析下,文字的颜色,是否会影响受试者的反应. 这里先看看什么是斯特鲁普效应: 斯特鲁普 ...
- Jersey 2 + Maven + Tomcat + IntelliJ IDEA 搭建RESTful服务
本文参考以下内容: [1] Starting out with Jersey & Apache Tomcat using IntelliJ [2] [Jersey]IntelliJ IDEA ...
- struts2实现改变超链接的显示方式
问题来源:我们看别人家专业的网站的时候,会发现他们的超链接都是这样的http://www.cnblogs.com/ivictor/p/5522383.html 这是一个连接到某一篇文章的超链接,我们一 ...
- WebStorm 11、PhpStorm 10免费激活(不需要注册码)
之前分享的WebStorm9.WebStrom10.PhpStorm9注册码生成网站已经被站长关了,比较遗憾!http://my.oschina.net/ximidao/blog/486353 现在官 ...