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复用的高效之道
382 0
|
21天前
|
网络协议 算法 Go
在go内置网络库中的路由和多路复用
【7月更文挑战第6天】本文介绍Go的`net/http`库提供基础的HTTP服务,`ListenAndServe`管理TCP连接,处理请求。处理程序默认使用`DefaultServeMux`。也可以选择多路复用模式ServeMux。它们的示例代码展示了自定义`ServeHTTP`结构体处理不同路由 。
35 2
|
22天前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
|
1月前
|
Go
如何在Go中进行文件操作以及如何使用协程来实现并发编程
如何在Go中进行文件操作以及如何使用协程来实现并发编程
24 2
|
1月前
|
Linux C++
c++高级篇(三) ——Linux下IO多路复用之poll模型
c++高级篇(三) ——Linux下IO多路复用之poll模型
|
1月前
|
缓存 监控 网络协议
c++高级篇(二) ——Linux下IO多路复用之select模型
c++高级篇(二) ——Linux下IO多路复用之select模型
|
1月前
|
存储 Go API
Go 语言基础之常用包【flag、time、strconv、io】(2)
Go 语言基础之常用包【flag、time、strconv、io】
|
1月前
|
存储 Unix Go
Go 语言基础之常用包【flag、time、strconv、io】(1)
Go 语言基础之常用包【flag、time、strconv、io】
|
2月前
|
安全 Go 调度
|
2月前
|
监控 负载均衡 算法
Golang深入浅出之-Go语言中的协程池设计与实现
【5月更文挑战第3天】本文探讨了Go语言中的协程池设计,用于管理goroutine并优化并发性能。协程池通过限制同时运行的goroutine数量防止资源耗尽,包括任务队列和工作协程两部分。基本实现思路涉及使用channel作为任务队列,固定数量的工作协程处理任务。文章还列举了一个简单的协程池实现示例,并讨论了常见问题如任务队列溢出、协程泄露和任务调度不均,提出了解决方案。通过合理设置缓冲区大小、确保资源释放、优化任务调度以及监控与调试,可以避免这些问题,提升系统性能和稳定性。
80 6