C语言中协程(coroutine)实现
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调度主循环处理
- 循环遍历coroutine就绪队列,依次运行coroutine.
- 如果没有就绪的coroutine且本地队列上coroutine个数为0,则进行步骤3,否则进行步骤4
- 通过条件变量等待分配新的coroutine,如果收到了条件变量且是退出指令,则进行步骤5,否则进行步骤1.
- 本地队列还有coroutine,但是coroutine都在等待事件,则进行事件循环以等待指定事件的到来,这样就会有coroutine就绪,进行步骤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)实现相关推荐
- Go语言中协程的概念和基本使用
为什么80%的码农都做不了架构师?>>> Go协程(Goroutine)是与其他函数同时运行的函数.可以认为Go协程是轻量级的线程.与创建线程相比,创建Go协程的成本很小.因此 ...
- C++20中的协程(Coroutine)
目录 C++20中的协程(Coroutine) 那么,什么是协程? 那么这么好用的协程,是不是只要C++20一推出,我们加上一个关键字就能直接把异步调用转化为同步调用呢? 协程函数和Awaitable ...
- [通用技术]在不同语言中用协程实现全排列算法(C++/Lua/Python/C#)
我这里实现全排列的基本算法如下(C++): 1 #include <algorithm> 2 #include <iostream> 3 #include <vector ...
- go 怎么等待所有的协程完成_GO语言基础进阶教程:Go语言的协程——Goroutine
Go语言的协程--Goroutine 进程(Process),线程(Thread),协程(Coroutine,也叫轻量级线程) 进程进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为&qu ...
- c++ 协程_理解Python协程(Coroutine)
由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...
- python3 协程 写法_理解Python的协程(Coroutine)
由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...
- c++ 协程_Python3 协程(coroutine)介绍
本文首发于 at7h 的个人博客. 目前 Python 语言的协程从实现来说可分为两类: 一种是基于传统生成器的协程,叫做 generator-based coroutines,通过包装 genera ...
- GDScript:协程(Coroutine)(二)简单粗暴实用至上的语法设计
<GDScript:协程(Coroutine)(一)概念和使用范例 >中介绍了GDScript中协程的概念和简单用法.写完之后总感觉GDScript的协程有哪里不对劲儿,刚才终于琢磨过来& ...
- tornado协程(coroutine)原理
tornado中的协程是如何工作的 本文将按以下结构进行组织,说明tornado中协程的执行原理 协程定义 生成器和yield语义 Future对象 ioloop对象 函数装饰器coroutine 总 ...
最新文章
- 关于AXI DMA CYCLIC 调试过程中的问题
- Linxu安装Tomcat与Jdk并卸载自带OpenJdk
- MySQL 优化原理(一)
- npm 打包vue,错误 errno 126 / 清空node_modules目录
- Nginx 安装与启动
- 用聚合数据API快速写出小程序
- 我们采访了小鹏G3「高温抑菌」项目负责人,发现智能车OTA并不简单
- 关于【cocos2dx-3.0beta-制作flappybird】教程在3.2project中出现找不到CCMenuItem.h的解决方法...
- java对象与json字符串的互相转换
- linux exec 脚本之家,详解Shell脚本中调用另一个Shell脚本的三种方式
- 用R语言进行数据可视化的综合指南(一)
- Android系统开机优化
- OpenKG 祝大家 2021 新年快乐 —「2020 精选文章汇编」
- ubuntu借助windows的网络共享上网
- 【数据结构】红黑树前置知识——4阶B树
- hadoop相关软件下载地址
- Access Violation(非法访问)问题解析
- 简要讨论Python对拼多多关键字搜索、拼多多商品详情页封装API接口对于电商爆款的作用
- 单行或者多行文本溢出展示省略号的实现方法
- 如何使用git与svn远程仓库协作