仿制云风的协程库的接口设计,我花了一个下午加晚上的时间重构了之前写的协程库,提供的接口现在和云风大大的协程接口一模一样,都是仿制lua的非对称协程。我们依旧没有用ucontext.h组件(因为ucontext.h组件在osX下已经deprecated了,如果你加入sys/ucontext.h头文件,发现某些小型C协程库还能运行,纯属巧合,因为官方已经不维护这个组件了,以后很可能出错),我们的协程库可以运行在兼容X86平台的操作系统上,各种unix-like操作系统,windows操作系统都可以,不过得用gcc或者clang或者与之兼容的mingw编译工具编译出32位的程序运行,不能用vs或者vc++系列,因为我们用了gasm内联汇编格式,当然你可以稍微改下几行汇编就能移植到vs或者vc++版本。在多线程环境下运行协程时,不同线程不能共享协程组,也就是说任意两个协程,若它们属于不同的线程,那么它们得属于不同的协程组。这是基本编程准则。比如想在POSIX多线程接口里头用我们的协程库时候,你可以这么用:

 1 #include <all needed>
2
3 hello(schedular *s)
4 {
5 coroutine_new(s, otherfunc, args); /* 在s协程组里头创建一个协程, 这个s可能是S或S1等等 */
6 ... do something
7 }
8
9 foo(...)
10 {
11 schedular *S1 =coroutine_open(); /* 分配一个协程组S1,这个只能属于线程tid */
12 int co = coroutine_new(S1, hello, S1); /* 创建一个协程 */
13 ... do something
14 coroutine_resume(S1,co); /* 调用本地协程组里头的co协程 */
15 ... do something
16 coroutine_close(&S1); /* 释放S1协程组 */
17 }
18
19 main{
20 schedular *S =coroutine_open(); /* 分配一个协程组S,这个只能属于主线程 */
21 pthread_create(tid, ..., foo, ...); /* 创建Posix线程 */
22 int co = coroutine_new(S, hello, S); /* 创建一个协程 */
23 ... do something
24 coroutine_resume(S,co); /* 调用本地协程组里头的co协程 */
25 ... do something
26 coroutine_close(&S); /* 释放S协程组 */
27 pthread_join(tid, ...); /* 等待并回收线程资源 */
28 }

  如果要想不同线程间的协程通讯,得用操作系统各自的API,比如用共享内存方式来实现,我们没有实现类似goroutine的channel。协程库为共享栈模式,一个协程组可以容纳最多一百万个协程,每个协程共用128Kbytes栈空间,我用top命令监测了一下运行一百万个协程的测试程序,此时该测试程序内存占用峰值为280M左右,可以推算每个协程内存占用峰值为280bytes左右。可以推断,如果运行一千万个协程,我们至少需要10个协程组,每个协程组280M,一共2800M = 2.7G左右 = 4G总理论空间 - 1G内核空间,所以我们的协程库所能支持的最大协程数是1000万,这是理论上限。用gprof测试程序性能得知,2999997次协程切换共用0.39秒,每次切换时间在130ns左右。最重要的是,我们的协程库所有的源码加起来大概只有400行左右,这还包括了微量的注释和头文件。如果能够移植到X64版本,那么我们的协程库可以轻松支持千万数量的协程,可以在实际开发中使用,不再是单单只有教学意义的小玩意了。可惜的是在unix like的各种平台下,作为唯一的异步非阻塞IO组件,linux kernal实现的aio组件并不是那么成熟(可能要烂尾了),而且glibc中用线程+信号模拟的用户态aio组件也是有很多bug存在。异步操作与协程的结合果然还是在语言层面(做编译器前端)实现更好,而非在库上实现。

项目GitHub链接:https://github.com/Yuandong-Chen/coroutine/tree/ezco.v.0.0.1

后记:

  最近用setjmp.h组件,重构了项目,删去了汇编代码,把项目移植到了X64-macosx-clang版本(只兼容intel X64的处理器,osX操作系统以及Clang编译器,不兼容其他任何变化,包括Linux,GCC,X86等等),可以支持上亿个协程(思考下?为何?)。项目放在上面GitHub链接里头的默认版本内。到此为止,我们的协程完成度近似libconcurrency库。如何移植到X64-linux-gcc版本是一个问题,因为glibc里头,我们无法在jmp_buf数组中通过偏移量取出rip逻辑寄存器的取值。所以为了达到可移植性,我们还是得用汇编写一个自己的setjmp/longjmp函数,其实很多协程库就是自己重写了setjmp/longjmp以满足兼容性。这样也有个坏处,那就是我们得写大量的汇编,对不同的C编译器,不同的操作系统,不同的CPU都得写一个对应的汇编版本,当然,可以通过内联汇编的方式在一定程度上减轻一点点工作量。这种兼容性的体力活我就不去干了。

  这里给出为何能支持上亿协程的答案:很简单,我们以1000万个协程占4G空间估计,那么64位机如果有128G内存的话,1000万*128/4 = 3亿左右的协程并发。当然,我想没人会用3亿协程并发,因为即便是8核16线程,负载为300000000/(16) = 1.8亿协程/线程,除非你有超级计算机,那么才可以做到几百协程/线程。

进一步需要做的:

1)彻底放弃共享栈,每个协程重新拥有自己独立的栈空间。因为x64下的虚拟内存足够大了,共享栈带来的优势太小,我们根本不需要几亿个协程并发,但是我们需要他们执行的足够快。

2)放弃lua的resume-yield协程模型,改用erlang的spawn模型,从而能够利用多核带来的并行执行的优势。

3)添加协程间的channel机制(一种消息传递机制)。

总的说来,我们相当于要把erlang的轻量级进程这部分做成C语言库。

写个百万级别full-stack小型协程库——原理介绍的更多相关文章

  1. C高级 跨平台协程库

    1.0 协程库引言 协程对于上层语言还是比较常见的. 例如C# 中 yield retrun, lua 中 coroutine.yield 等来构建同步并发的程序. 本文就是探讨如何从底层实现开发级别 ...

  2. 一个“蝇量级” C 语言协程库

    协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...

  3. 协程库st(state threads library)原理解析

    协程库state threads library(以下简称st)是一个基于setjmp/longjmp实现的C语言版用户线程库或协程库(user level thread). 这里有一个基本的协程例子 ...

  4. CPU的最小执行单位是线程,协程不需要qt支持...直接用现成的协程库就行了

    协程也就在I/O操作上才有优势,Qt事件循环,本事很多I/O已经是异步了,利用好异步(虽然都说异步有点反人类思维).因为CPU的执行最小单位是线程,协程也只是在其之上又调度而已. 我的意思是利用好异步 ...

  5. 二、深入asyncio协程(任务对象,协程调用原理,协程并发)

      由于才开始写博客,之前都是写笔记自己看,所以可能会存在表述不清,过于啰嗦等各种各样的问题,有什么疑问或者批评欢迎在评论区留言. 如果你初次接触协程,请先阅读上一篇文章初识asyncio协程对asy ...

  6. 协程的原理(Coroutine Theory)

    原文链接:https://lewissbaker.github.io/2017/09/25/coroutine-theory This is the first of a series of post ...

  7. Stackful 协程库 libgo(单机100万协程)

    libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...

  8. 基于ASIO的协程库orchid简介

    什么是orchid? orchid是一个构建于boost库基础上的C++库,类似于python下的gevent/eventlet,为用户提供基于协程的并发模型. 什么是协程: 协程,即协作式程序,其思 ...

  9. libco协程库上下文切换原理详解

    缘起 libco 协程库在单个线程中实现了多个协程的创建和切换.按照我们通常的编程思路,单个线程中的程序执行流程通常是顺序的,调用函数同样也是 “调用——返回”,每次都是从函数的入口处开始执行.而li ...

随机推荐

  1. this web application instance has been stopped already.

    this web application instance has been stopped already. Could not load oracle/sql/converter_xcharset ...

  2. MSMQ队列学习记录

    微软消息队列-MicroSoft Message Queue(MSMQ) 使用感受:简单. 一.windows安装MSMQ服务 控制面板->控制面板->所有控制面板项->程序和功能- ...

  3. 热爱H5

    我不去想是否能够功成名就既然选择了前端便只顾埋头于无声 我不去想能否赢得掌声既然钟情于IT就勇敢得踏步前行 我不去想前行的路上会不会日暮穷途既然目标是未来留给现在的只能是汗水 我不去想所谓的成功是不是 ...

  4. Web性能优化工具WebPageTest(三)——本地部署(Windows 7版本)

    这次先能够使用PC端的浏览器测试,首先需要下载官方的发布版本"WebPageTest 3.0". 1. agent:浏览器代理软件 2. mobile:移动端参数相关代码 3. w ...

  5. 探讨.NET Core数据加密和解密问题

    前言 一直困扰着我关于数据加密这一块,24号晚上用了接近3个小时去完成一项任务,本以为立马能解决,但是为了保证数据的安全性,我们开始去对数据进行加密,然后接下来3个小时专门去研究加密这一块,然而用着用 ...

  6. js里变量的作用域

    一.在js中,变量的定义并不是以代码块作为作用域的,而是以函数作为作用域.也就是说,如果变量是在某个函数中定义的,那么,它在函数以外的地方是不可见的.但是,如果该变量是定义在if或者for这样的代码块 ...

  7. bzoj4825 [Hnoi2017]单旋

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...

  8. 04(2) 基于上下文相关的GMM-HMM声学模型2之参数共享

    1.三音素建模存在的问题 问题一:很多三音素在训练数据中没有出现(尤其跨词三音素) 问题二:在训练数据中出现过的三音素有相当一部分出现的频次较少 因此,三音素模型训练时存在较严重的数据不足问题 2.参 ...

  9. codeforces 782B The Meeting Place Cannot Be Changed (三分)

    The Meeting Place Cannot Be Changed Problem Description The main road in Bytecity is a straight line ...

  10. Gym - 101102C线段树

    Judge Bahosain was bored at ACM AmrahCPC 2016 as the winner of the contest had the first rank from t ...