字节一面:go的协程比线程轻量,体现在哪?

简介: 这里特意指出网络IO操作不会走上图的模型,否则要分配的系统线程M依然很多,程序很快就爆满了。这时G会和逻辑处理器P分离,并移动到netpoller,一旦网络轮询器通知网络读/写就绪,对应G就会重新分配到逻辑器处理器上来完成操作, 在此期间原系统线程M可以去做别的G。

01

用户态和内核态

 

Linux整个体系分为用户态和内核态(或者叫用户空间和内核空间), 那内核态究竟是什么呢?


本质上我们所说的内核态, 它是一种特殊的软件程序,特殊在哪?统筹计算机的硬件资源,例如协调CPU资源、分配内存资源、并且提供稳定的环境供应用程序运行。


应用程序系统调用坠入内核态。


0b7630fac2021deb3c3da356aa7c1e7f.png


02

为什么线程切换会导致用户态和内核态的切换?


线程是cpu调度的基本单位,进程是资源占有的基本单位。


因为线程中的代码是在用户态运行,而线程的调度是在内核态,所以线程切换会触发用户态和内核态的切换。


线程上下文切换的代价是高昂的:上下文切换的延迟取决于不同的因素,大概是50到100 ns左右,考虑到硬件平均在每个核心上每ns执行12条指令,那么一次上下文切换可能会花费600到1200条指令的延迟时间。


03

导致线程上下文切换的时机


<1>. 自发性上下文切换 自发性上下文切换指线程由于自身因素导致的切出
Thread.sleep() 线程主动休眠
object.wait() 线程等待锁
Thread.yield() 当前线程主动让出CPU,如果有其他就绪线程就执行其他线程,如果没有则继续当前线程
oThread.join() 阻塞发起调用的线程,直到oThread执行完毕


<2>. 非自发性上下文切换:线程由于线程调度器的原因被迫切出
线程的时间片用完
高优先级线程抢占
垃圾回收动作


04

线程切换的开销


<1>. 直接开销
保存/恢复上下文所需的开销
线程调度器调度线程的开销
<2>. 间接开销
重新加载高速缓存
上下文切换可能导致 一级缓存被冲刷,写入下一级缓存或内存


bde4d0a9b83b949e105c5ae36876e663.png


05

go的协程轻量级体现在哪?


如上面所述,线程切换会导致 用户态和内核态的切换,其中内核态耗时较长,且不受用户代码控制。


go将goroutine的调度维持在用户态, 这是由GPM中的P Process来完成的,做用户态任务的调度器,功能类比于常规的操作系统线程调度器,所以又被称为逻辑处理器。


c971e42e872165cadf4c0a790cfc94bb.png


(1) 上下文切换代价小:  P 是G、M之间的桥梁,调度器对于goroutine的调度,很明显也会有切换,这个切换是很轻量的:只涉及PC SP DX三个寄存器的值的修改;而对比线程的上下文切换则需要陷入内核模式、以及16个寄存器的刷新。


(2) 内存占用少: 线程栈空间通常是2M, Goroutine栈空间最小是2k:


Golang可以轻松支持10W+的Goroutine运行而线程数量达到1k,内存占用就到2G。


06

Go GMP调度方式


由逻辑处理器P调度协程G进系统线程M (若本地队列没有G,从其他队列/全局队列偷取G),线程M执行G, 遇到[系统调用], G和M分离,拿新的M去接管原逻辑处理器P


2ff04b3d3f78d703dfe991e20a31d4a7.png


请仔细阅读上图,出处不可考证,感谢原图作者。


这里特意指出网络IO操作不会走上图的模型,否则要分配的系统线程M依然很多,程序很快就爆满了。这时G会和逻辑处理器P分离,并移动到netpoller,一旦网络轮询器通知网络读/写就绪,对应G就会重新分配到逻辑器处理器上来完成操作, 在此期间原系统线程M可以去做别的G。

相关文章
|
6月前
|
Kubernetes Linux Go
使用 Uber automaxprocs 正确设置 Go 程序线程数
`automaxprocs` 包就是专门用来解决此问题的,并且用法非常简单,只需要使用匿名导入的方式 `import _ "go.uber.org/automaxprocs"` 一行代码即可搞定。
306 78
|
3月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
4月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
4月前
|
人工智能 Java Linux
Go 调度器:一个线程的执行流程
本文详细解析了Go语言运行时调度器的初始化流程,重点介绍了GMP模型的构建过程。内容涵盖调度器初始化函数`runtime·schedinit`、线程与处理器的绑定、P结构体的创建与初始化,以及主Goroutine的启动流程。通过源码分析,帮助读者深入理解Go运行时的底层机制。
110 0
|
消息中间件 并行计算 安全
进程、线程、协程
【10月更文挑战第16天】进程、线程和协程是计算机程序执行的三种基本形式。进程是操作系统资源分配和调度的基本单位,具有独立的内存空间,稳定性高但资源消耗大。线程是进程内的执行单元,共享内存,轻量级且并发性好,但同步复杂。协程是用户态的轻量级调度单位,适用于高并发和IO密集型任务,资源消耗最小,但不支持多核并行。
261 1
|
存储 安全 算法
Go语言是如何支持多线程的
【10月更文挑战第21】Go语言是如何支持多线程的
255 72
|
10月前
|
消息中间件 调度
如何区分进程、线程和协程?看这篇就够了!
本课程主要探讨操作系统中的进程、线程和协程的区别。进程是资源分配的基本单位,具有独立性和隔离性;线程是CPU调度的基本单位,轻量且共享资源,适合并发执行;协程更轻量,由程序自身调度,适合I/O密集型任务。通过学习这些概念,可以更好地理解和应用它们,以实现最优的性能和资源利用。
345 11
|
存储 消息中间件 人工智能
进程,线程,协程 - 你了解多少?
本故事采用简洁明了的对话方式,尽洪荒之力让你在轻松无负担的氛围中,稍微深入地理解进程、线程和协程的相关原理知识
119 2
进程,线程,协程 - 你了解多少?
|
Go 调度 开发者
Go语言多线程的优势
【10月更文挑战第15天】
134 4
|
运维 监控 Go
go语言轻量化
【10月更文挑战第16天】
110 3

热门文章

最新文章