一、协程简介
1、简介
协程(coroutine)不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。一个程序可以包含多个协程,可以对比与一个进程包含多个线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制。而协程也相对独立,有自己的上下文,但是其切换由自己控制,当前协程切换到其他协程由当前协程来控制。
协程调度单位减小到函数,上下文切换不需要内核参与,不存在系统调用。上下文切换开销降到最低,系统调用降到最低,没有锁竞争,没有信号处理。保留了程序对请求的线性处理逻辑,提高了程序的开发效率,可扩展性和可维护性。协程是一种程序组件。通常我们把协程理解为是一种程序自己实现调度、用于提高运行效率、降低开发复杂度的东西。而State-Thread(以下简称st),就是一个由C语言编写的小巧、简洁却高效的开源协程库。这个库基于单线程运作、不强制占用用户线程,给予了开发者最大程度的轻量级和较低的侵入性。
很多语言都拥有协程,例如python或者golang。而对于c/c++而言,通常实现协程的常见方式,通常是依赖于glibc提供的setjump&longjump或者基于汇编语言,当然还有基于语义实现(protothread)。linux上使用协程库的方式,通常也会分为替换函数和更为暴力的替换so来实现。当然而各种方式有各自的优劣。而st选用的汇编语言实现setjump&longjump和要求用户调用st_打头的函数来嵌入程序。所以st具备了跨平台的能力,以及让开发者们更开心的“与允许调用者自行选择切换时机”的能力。随着coroutine ts正式进入c++20,c++已经进入协程时代了。但是不幸的是c++20的协程标准只包含编译器需要实现的底层功能,并没有包含简单方便地使用协程的高级库,相关的类和函数进入std标准库估计要等到c++23。本文先以协程库(State Threads)来讲解c++中的协程。
2、state threads优缺点
优点:
- 允许设计快速且高效可扩展的应用程序。应用程序将在CPU的负载和数量上都能很好的扩展。
- 简化了应用程序的编程和调试,因为通常不需要互斥锁定,并且整个应用程序都可以自由适应静态变量和不可重入的库函数。
缺点:
- 套接字上的所有I/O操作都必须适应st库的I/O函数,因为只有那些函数才能执行线程调度并阻止应用程序的进程被阻塞。
3、state threads工作流程
在宏观的来看,ST的结构主要分成:
- vp_schedule。主要是负责了一个调度的能力。有点类似于linux内核当中的schedule()函数。每次当这个函数被调用的时候,都会完成一次线程的切换。
- 各种Queue。用于保存各种状态下等待被调度协程(st_thread)
- Timer。用于记录各种超时和sleep。
- poll。用于监听各种io事件,会根据系统能力不同而进行切换(kqueue、epoll、poll、select)。
- st_thread。用于保存各种协程的信息。
其中比较重要的是schedule模块和thread模块两者。这两者实现了一个完整的协程切换和调度。属于st的核心。而schedule部分通常是开发者们最需要关心的部分。
二、具体实现
0、库的下载
https://sourceforge.net/projects/state-threads/
解压:tar zxvf st-1.9.tar.gz
编译:make linux-debug
解压并编译后的结果如下:
(st-1.9)解压后文件简介:
(1)doc目录
- st.html——ST库概论
- timeout_heap.txt——超时heap实现
- notes.html——给出了编程注意点,包括移植,信号,进程内同步,进程间同步,非网络IO,超时处理,特别谈到进程内同步非常简单,不需要同步资源;非网络IO中谈到drawback和设计时需要避免的方法
- reference.html——一个API接口文档介绍
(2)example目录
- server:包含server.c和error.c。server接受一个客户端连接,接收客户端数据并返给客户端一个简单的HTML网页
- lookupdns:包含lookupdns.c和res.c。
- proxy:包含proxy.c。
st程序结构:
st底层基于event-driven select/poll/kqueue/epoll等IO复用机制。下面以e