C语言协程库实现说明

代码实现

1. 当前支持的功能概览

1.1 创建任意数量协程并在协程中yield

#include <stdio.h>
#include <stdlib.h>#include <pthread.h>#include "gtest/gtest.h"#include "coroutine.h"
#include "asyncio.h"static int g_run_sums = 0;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;static void co(void *args)
{int num = *((int *)args);pthread_mutex_lock(&g_lock);g_run_sums += num;  // 每个协程分别递增全局变量pthread_mutex_unlock(&g_lock);printf("coroutine:%d begin...\r\n", num);coroutine_yield();printf("coroutine:%d ended...\r\n", num);
}TEST(coroutine_create, three_cos_run_success)
{int a = 1, b = 2, c = 3; //a, b, c三个协程依次对全局变量g_run_sums增加1,2,3coroutine_init();coroutine_create(co, (void*)&a);coroutine_create(co, (void*)&b);coroutine_create(co, (void*)&c);coroutine_loop();EXPECT_EQ(g_run_sums, 6); // 最终全局变量为6
}

1.2 创建2个协程,其中一个睡眠100ms


#include <stdio.h>
#include <stdlib.h>#include <pthread.h>#include "gtest/gtest.h"#include "coroutine.h"
#include "asyncio.h"static int seq = 0;
static int co_seq[2] = {0};static void co_sleep(void *args)
{printf("co sleep begin.\r\n");asyncio_sleep(100); // 调用asyncio api睡眠100ms.co_seq[seq++] = 100;printf("co sleep end.\r\n");
}static void co_nosleep(void *args)
{printf("co no sleep begin.\r\n");co_seq[seq++] = 200; printf("co no sleep end.\r\n");
}TEST(coroutine_run, co_sleep)
{coroutine_init();coroutine_create(co_sleep, NULL);coroutine_create(co_nosleep, NULL);coroutine_loop();EXPECT_EQ(co_seq[0], 200); //验证未睡眠协程先完成了执行EXPECT_EQ(co_seq[1], 100);
}

2. COROUTINE状态

corouting有3种状态, RUNNALBE, RUNNING, WAITING.

  • WAITING: corouting暂停并等待一些条件以继续运行.比如:sleep, 系统调用,同步操作(原子或锁操作),这种延迟是性能差的根源.
  • RUNNABLE: corouting具备运行条件正在等待分配processor以执行指令
  • RUNNING: corouting已经分配到processor,并正在上面执行指令

3. 调度器实现

  • processor_t: 管理一个实际的os线程,内部使用队列维护分配给它的coroutine,使用epoll进行事件循环.
  • processors_t: 全局processor_t管理器,创建时会按照实际的cpu个数创建对应的processor_t, 它负责将新协程按照一定算法分配给某个processor_t.
    同时负责检测没有任何协程时退出进程.

3.1 主进程何时退出

当没有任何协程存在时,则退出主进程.

3.1.1 实现原理

模拟实现了Golang中的waitGroup, 用于等待所有协程退出.新协程创建会调用waitGroup_add,协程结束会调用waitGroup_del,当waitGroup空闲时
则说明所有协程都已经退出.

3.2 processor_t调度主循环处理

    1. 循环遍历coroutine就绪队列,依次运行coroutine.
    1. 如果没有就绪的coroutine且本地队列上coroutine个数为0,则进行步骤3,否则进行步骤4
    1. 通过条件变量等待分配新的coroutine,如果收到了条件变量且是退出指令,则进行步骤5,否则进行步骤1.
    1. 本地队列还有coroutine,但是coroutine都在等待事件,则进行事件循环以等待指定事件的到来,这样就会有coroutine就绪,进行步骤1.
    1. 退出主循环

3.3 上下文切换实现

3.3.1 原理

corouting在用户态进行上下文切换,上下文主要包括:堆栈,寄存器(IP, SP等).
上下文切换主要通过<ucontext.h>中定义的getcontext, setcontext, makecontext, swapcontext实现.

3.3.2 上下文切换时机

  • corouting主动调用coroutine_yield(),如果有其它待运行的coroutine则主动让出processor_t
  • 协程中调用了协程库asyncio API,则由API选择合适的时机进行上下文切换,如调用阻塞API,如corouting_sleep.
  • 如果你在协程中执行cpu密集型操作或直接调用阻塞的C api,那么会影响当前processor的调度和运行.

3.3.3 堆栈使用

  • 每个processor_t维护1M的堆栈空间M
  • 协程刚创建时为RUNNABLE状态,此时直接使用M作为堆栈,当协程需要放权时保存当前堆栈到协程自己的空间M0
  • 协程恢复运行时,将保存的堆栈M0还原到M中继续运行

这样每个协程最大都可以有1M的堆栈空间,且堆栈空间能够按需分配,每个processor_t上堆栈的消耗为所有协程
实际使用的堆栈内存+1M.

如果不这样实现,每个协程都需要初始分配1M空间,消耗为协程个数*1M.

4. 异步操作协程库asyncio实现

  • asyncio提供一系列api用于在协程环境中编写并发代码.
  • asyncio是coroutine框架提供的api可以用于实现高性能网络服务器,数据库连接库,分布式任务队列等.
  • asyncio适合IO密集型和高级别的结构化网络程序

4.1 当前支持的API

  • coroutine_sleep(long delay_ms): 当前协程休眠指定ms.
  • coroutine_yield(): 当前协程主动放权给其它就绪协程,由调度器选择合适时机再重新调度.

5. 代码说明

5.1 编译代码


cmake -H. -Bbuild
cmake --build ./build

5.2 运行测试

cd build
ctest --verbose

C语言中协程(coroutine)实现相关推荐

  1. Go语言中协程的概念和基本使用

    为什么80%的码农都做不了架构师?>>>    Go协程(Goroutine)是与其他函数同时运行的函数.可以认为Go协程是轻量级的线程.与创建线程相比,创建Go协程的成本很小.因此 ...

  2. C++20中的协程(Coroutine)

    目录 C++20中的协程(Coroutine) 那么,什么是协程? 那么这么好用的协程,是不是只要C++20一推出,我们加上一个关键字就能直接把异步调用转化为同步调用呢? 协程函数和Awaitable ...

  3. [通用技术]在不同语言中用协程实现全排列算法(C++/Lua/Python/C#)

    我这里实现全排列的基本算法如下(C++): 1 #include <algorithm> 2 #include <iostream> 3 #include <vector ...

  4. go 怎么等待所有的协程完成_GO语言基础进阶教程:Go语言的协程——Goroutine

    Go语言的协程--Goroutine 进程(Process),线程(Thread),协程(Coroutine,也叫轻量级线程) 进程进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为&qu ...

  5. c++ 协程_理解Python协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  6. python3 协程 写法_理解Python的协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  7. c++ 协程_Python3 协程(coroutine)介绍

    本文首发于 at7h 的个人博客. 目前 Python 语言的协程从实现来说可分为两类: 一种是基于传统生成器的协程,叫做 generator-based coroutines,通过包装 genera ...

  8. GDScript:协程(Coroutine)(二)简单粗暴实用至上的语法设计

    <GDScript:协程(Coroutine)(一)概念和使用范例 >中介绍了GDScript中协程的概念和简单用法.写完之后总感觉GDScript的协程有哪里不对劲儿,刚才终于琢磨过来& ...

  9. tornado协程(coroutine)原理

    tornado中的协程是如何工作的 本文将按以下结构进行组织,说明tornado中协程的执行原理 协程定义 生成器和yield语义 Future对象 ioloop对象 函数装饰器coroutine 总 ...

最新文章

  1. 关于AXI DMA CYCLIC 调试过程中的问题
  2. Linxu安装Tomcat与Jdk并卸载自带OpenJdk
  3. MySQL 优化原理(一)
  4. npm 打包vue,错误 errno 126 / 清空node_modules目录
  5. Nginx 安装与启动
  6. 用聚合数据API快速写出小程序
  7. 我们采访了小鹏G3「高温抑菌」项目负责人,发现智能车OTA并不简单
  8. 关于【cocos2dx-3.0beta-制作flappybird】教程在3.2project中出现找不到CCMenuItem.h的解决方法...
  9. java对象与json字符串的互相转换
  10. linux exec 脚本之家,详解Shell脚本中调用另一个Shell脚本的三种方式
  11. 用R语言进行数据可视化的综合指南(一)
  12. Android系统开机优化
  13. OpenKG 祝大家 2021 新年快乐 —「2020 精选文章汇编」
  14. ubuntu借助windows的网络共享上网
  15. 【数据结构】红黑树前置知识——4阶B树
  16. hadoop相关软件下载地址
  17. Access Violation(非法访问)问题解析
  18. 简要讨论Python对拼多多关键字搜索、拼多多商品详情页封装API接口对于电商爆款的作用
  19. 单行或者多行文本溢出展示省略号的实现方法
  20. 如何使用git与svn远程仓库协作

热门文章

  1. TechCrunch创始人宣布成立1亿美元XRP对冲基金
  2. e—mis信息管理系统_管理信息系统(MIS)的目标
  3. 蓝桥杯 油漆面积【第八届】【省赛】【A组】线段树扫面线/求矩形相交面积/模拟
  4. 【C++】「一本通 1.1 例 5」智力大冲浪
  5. 【9605】智力大冲浪
  6. 毕业设计之 ---- 基于大数据挖掘分析的北京二手房数据分析
  7. Scope Hoisting(范围提升)
  8. 沉迷百度前端技术学院的第一天
  9. 为了彻底弄懂CSS中的1px究竟有多长,我翻出了家里的卷尺
  10. 相联存储器的工作原理(按内容访存,cache、快表中应用)