🗒️如何理解协程-以st-thread为例
2023-7-11
| 2023-7-27
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password

如何理解协程

notion image
协程相比之与进程、线程,更像是用户自己实现的库函数,是在操作系统之上的操作,保存堆栈上下文信息。
二.进程、线程、协程三者的上下文切换比较
进程
线程
协程
切换者
操作系统
操作系统
用户(编程者/应用程序
切换时机
根据操作系统自己的切换策略,用户不感知
根据操作系统自己的切换策略,用户不感知
用户自己的程序决定
切换内容
页全局目录,内核栈,硬件上下文
内核栈,硬件上下文
硬件上下文
切换内容的保存
保存于内核栈中
保存于内核栈中
保存在用户自己的变量(用户栈或堆)
切换过程
用户态 - 内核态 - 用户态
用户态 - 内核态 - 用户态
用户态(没用进入内核态)
切换效率

协程的利与弊

代码风格更易于理解,代码复用更方便
协程最大的问题就是无法利用CPU多核

协程应用场景

一个线程内的多个协程是串行执行的,不能利用多核,所以,显然,协程不适合计算密集型的场景。协程适合I/O 阻塞型。 I/O本身就是阻塞型的(相较于CPU的时间世界而言)。就目前而言,无论I/O的速度多快,也比不上CPU的速度,所以一个I/O相关的程序,当其在进行I/O操作时候,CPU实际上是空闲的。 我们假设这样的场景,如下图:1个线程有5个I/O的事情(子程序)要处理。如果我们绝对的串行化,那么当其中一个I/O阻塞时,其他4个I/O并不能得到执行,因为程序是绝对串行的,5个I/O必须一个一个排队等待处理,当一个I/O阻塞时,其它4个也得等着。

关于 State-Thread 的一个问题思考

关于st源码分析-运行协程文章 阅读中的一个思考,文中指出 State-Thread 在执行restoreWork的时候,在内部封装recevfrom,函数,主要是在idle主协程中,判断切换所有协程的sendto,发送域名查询请求后,再切换到idl来进行阻塞,其根本原理同手动执行所有sendto,再执行select/epoll一样,只是协程的st_sendto跟st_recvfrom看起来是顺序执行的。
如果说state thread 协程其实本质是对系统的select /epoll 进行封装的话,那么在什么时候进行切换是由库决定还是必须人工想好每个切换点,然后利用协程的切换去做这个事情。
除了 idle 协程,其他所有协程做的事情都是对于 在 系统线程 看来都是非阻塞,非阻塞协程干的话,就是把 需要监听的 fd 放进去全局变量 fd_set,然后由 idle 协程 执行 select() 函数阻塞,等待信号。ST的调度策略是 _ST_RUNQ 里面已经没有协程要运行了,才会切换到 idle 协程来阻塞。

State-Thread——Sleep

由于协程实际上是在单线程中运行,如果需要sleep而调用系统的sleep或者usleep的话会阻塞整个线程,所以协程内部需要实现自己的sleep, state-Thread是这样做的:

st_usleep(st_utime_t usecs)

_st_idle_thread_start(void *arg)

  1. 通过 引入一个协程状态 ST_ST_SLEEPING
  1. 将a协程加入SLEEPQ队列中,然后使用_ST_SWITCH_CONTEXT()切换其他协程,此时协程a的状态相当于挂起
  1. 在最终的_st_idle_thread_start()中,通过_st_vp_check_clock()处理所有进入_ST_ST_SLEEPING状态的协程,通过比对a协程加入SLEEPQ队列的时间值是否满足睡眠时间来做出下一步决断。

State-Thread——协程通信

 
技术分享
  • 技术研究
  • C++
  • 协程
  • 操作系统
  • RTP的H264载荷分析Git 如何在fork项目上Pull Push
    • Twikoo
    • Giscus
    目录