千家信息网

C语言怎么实现协程

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要介绍"C语言怎么实现协程"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"C语言怎么实现协程"文章能帮助大家解决问题。协程是一种用户空间的非抢占式线程
千家信息网最后更新 2025年01月19日C语言怎么实现协程

这篇文章主要介绍"C语言怎么实现协程"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"C语言怎么实现协程"文章能帮助大家解决问题。

协程是一种用户空间的非抢占式线程,主要用来解决等待大量的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 (1)    {        printf("hello world\n");        crReturn(0);    }    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的值,使其指向这个我自己创建的调用栈。因此在以后运行的时候,这个协程就会使用我提供的那块内存作为栈。

我的这个协程库提供了三个接口:

  1. coro_new:创建一个协程

  2. coro_yield:将控制权返回给调度协程

  3. coro_main:运行调度协程

协程的控制流程如下:

  1. 通过coro_main运行调度协程,并找出下一个运行的协程,运行之。

  2. 运行这个协程直到其调用coro_yield将控制权返还给调度协程。

  3. 重复以上两个步骤,直到所有协程运行完毕。

这个协程库实现的非常简单,只有100来行的代码,当然实现它的目的是为了提供一个最简单的协程模型,而不是一个功能完整、鲁棒性强的能投入实际业务运行的协程。

因此问题还是有很多的:

  1. 比如当在协程里面调用栈超过2M时,这个是需要处理的,现在的代码是没有做的,理应中断程序,避免写坏堆,产生随机的不可重现的问题。

  2. 显然在实现时没有考虑到多线程,如果在多线程环境里面运行,需要代码做同步处理。

  3. 现在的这个版本的协程有一个约定,在协程里调用的函数不能阻塞在syscall,这显然也是不科学的。一个完整的协程库,应该包含一些常用的syscall的非阻塞的实现,毕竟只有一个线程不能真的阻塞在这个调用上。

关于"C语言怎么实现协程"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注行业资讯频道,小编每天都会为大家更新不同的知识点。

函数 线程 运行 代码 问题 寄存器 时候 调度 阻塞 语言 上下 上下文 两个 做法 实际 知识 同步 处理 控制 不用 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 直销银行软件开发 中央网络安全和信息化领导小组组长 协同软件开发的服务商 服务器租用托管销售工作 网络安全销售部工作总结 数据库下面标识符不合法的是 肇庆地产软件开发公司 软件开发项目框架协议 plsql 数据库名 河南河马网络技术有限公司吧 河南信息化软件开发价格优惠 知识产权数据库智能检索云平台 怎么用云服务器做一个简历 小型数据中心的服务器数量 网络安全图片高清大全 乡镇党外人事人才数据库 增量服务器 电脑登录云服务器方法 assecc数据库创建表时在 ps4彩虹六号换服务器 爱客服务器安全性 相比其他商用关系型数据库产品 冬奥网络安全保障讲话 创业之星怎么登陆服务器 mysql数据库如何创建存储过程 网络安全文明教育相关图片 微信查违章服务器故障紧急修复 苹果手机数据库错误 大话2经典版2021最火服务器 ros web服务器
0