Go学习笔记-协程和IO多路复用

简介: Go学习笔记-协程和IO多路复用

1、什么是协程?

  • 进程的虚拟地址空间划分为用户空间和内核空间
  • 线程是进程中的执行体,拥有一个执行入口,以及从虚拟地址空间中分配的栈(包括用户栈和内核栈)
  • 操作系统会记录线程控制信息,线程在获得时间片就可以被执行,CPU的指令指针和栈指针就会记录和执行线程相关的信息
  • 当线程创建了很多个执行体,并且也给这些执行体指定入口和分配栈内存,就可以按需调度这些执行体
  • 线程为了控制这些执行体的切换,也会记录这些执行体的信息,如ID、栈的位置、执行入口、执行现场等数据
  • 上述这种由线程创建的执行体就被称为协程,操作系统对协程一无所知,所以协程也可被称为用户态线程


2、协程间切换

  • 当用户线程获得CPU的时间片后,可以创建很多协程
  • 某个协程让出执行权时,会有协程栈保存执行现场等信息,接着就可以切换到其他协程
  • 协程获得执行权时,会根据之前协程栈保存的现场信息数据恢复执行
  • 协程是由用户态调度的多任务模型

3、协程和IO多路复用

  • 高并发成为为主流趋势,多进程模型请求下内存资源紧张
  • 多进程模型下,内核态和用户态切换两头忙
  • 协程这种用户态调度模型受到了关注
  • 协程和IO多路复用的结合成为很好的高并发解决方案

4、IO多路复用

  • 通过操作系统的进程控制信息可以找到进程打开文件描述信息、创建socket信息等
  • socket的操作都由操作系统来完成,需要用户程序通过系统调用来完成
  • 每创建一个socket,就会在打开的文件描述符表中创建一条记录信息,返回给用户程序的是socket描述符
  • 每个TCP socket被创建时,操作系统都会在内核空间分配度缓冲区、写缓冲区
  • 获取响应数据时,需要从读缓冲区拷贝数据
  • 要通过socket发送数据时,先要把数据拷贝到写缓冲区

  • 问题来了:
  • 当用户程序想要从读缓冲区获取数据,不一定有
  • 当用户程序想要发送数据的时候,写缓冲区,不一定有空间
  • 解决办法:
  • 阻塞式IO
  • 让出CPU进到等待队列里,等socket准备就绪,再次获得时间片时就可以继续执行了
  • 要处理一个socket,就需要占用一个线程,处理完才能继续下一个
  • 高并发场景下,开销很大
  • 非阻塞式IO
  • 不让出CPU,但需要一直询问socket是否准备就绪
  • 但这种忙等待的方式可能会空耗CPU,增加响应延迟
  • IO多路复用
  • 操作系统把需要等待的socket加入到监听集合
  • 用户程序就可以通过一次系统调用同时监听多个socket
  • Linux提供三种IO多路复用方式:
  • select
  • 可以设置要等待的描述符,也可以设置要等待的超时时间
  • 如果有准备好的fd,或到了超时时间,select系统函数就会返回
  • select系统函数支持可读、可写、可执行
  • fd_set是一个unsigned long数组,16个元素,每一位对应一个fd,最多可以监听1024个
  • 并且每次调用都需要传递所有监听的集合,需要频繁的从用户态到内核态拷贝数据
  • 每次都需要遍历整个监听集合判断可操作的fd

  • poll
  • 只能解决最大监听fd描述符的个数,其他问题依然没有得到解决

  • epoll
  • epoll提供三个关键接口
  • epoll_create用于创建epoll句柄
  • epoll_ctl用于添加或删除fd与对应的事件信息,可以指定要监听的fd和事件类型,可以传入额外的data数据,每次只需要传入一个fd,无需传入所有fd集合
  • epoll_wait返回fd集合都是已经准备就绪的,这样就不会阻塞和空耗CPU了
  • epoll有什么问题:
  • 比如当某个线程读fd时,可能读到一半时,CPU时间片就到了,需要让出执行权,保存现场数据
  • 需要等到下一次时间片的CPU执行权时,才能恢复现场继续执行
  • 解决办法:
  • 频繁切换线程,需要保存和恢复执行现场
  • 可以交给协程来处理,对于CPU来说同一个线程的多个协程无感知
  • 协程拥有自己的栈空间,用于保存和恢复现场相对比较容易,和具体的线程业务逻辑解耦了
相关文章
|
2月前
|
存储 监控 Linux
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
【Linux IO多路复用 】 Linux下select函数全解析:驾驭I-O复用的高效之道
56 0
|
3月前
|
监控 安全 Java
Go语言学习笔记(一)
Go语言学习笔记(一)
106 1
|
15小时前
|
安全 Go
Golang深入浅出之-Go语言标准库中的文件读写:io/ioutil包
【4月更文挑战第27天】Go语言的`io/ioutil`包提供简单文件读写,适合小文件操作。本文聚焦`ReadFile`和`WriteFile`函数,讨论错误处理、文件权限、大文件处理和编码问题。避免错误的关键在于检查错误、设置合适权限、采用流式读写及处理编码。遵循这些最佳实践能提升代码稳定性。
5 0
|
2天前
|
存储 Go
用Go语言实现一个单协程消费者模型
用Go语言实现一个单协程消费者模型
15 0
|
2天前
|
网络协议 Linux Go
基于Go语言的网络IO扫盲
基于Go语言的网络IO扫盲
10 0
|
2天前
|
网络协议 Java Linux
go语言如何实现协程的抢占式调度的?
go语言如何实现协程的抢占式调度的?
10 0
|
2天前
|
Go 调度
go语言是如何实现协程的
go语言是如何实现协程的
7 0
|
4天前
|
安全 算法 Java
写给Java开发的Go语言协程实践
写给Java开发的Go语言协程实践
10 0
|
5天前
|
安全 Go 调度
|
2月前
|
NoSQL Java Linux
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
66 0