线程与协程的区别是什么?
协程是线程的再细分。
接下来会从三个方面解答这一问题:内存消耗、创建和销毁、切换
内存消耗
创建一个线程需要1MB栈内存,而一个协程仅需2KB。
创建和销毁
在操作系统中,有用户态和内核态两个概念。线程有内核级的线程和用户级的线程,但是最终还是涉及到了内核级。而协程Go的runtime负责管理,是用户级的。
切换
线程切换要保存各类寄存器,而协程只需要看三个:
- 程序计数器
- 栈指针
- 基址指针
关于Go的调度器
了解调度所需的三个结构体吗?
- G;协程
- M:线程
- P:虚拟的处理器
- sched:维持调度器的运行
大致讲讲这个过程?
runtime起始时会启动一些协程,并通过启动线程来开始协程的运行;协程越多,对应的承载它的线程也越多(M-N模型);每个虚拟处理器维护一个出于运行状态的协程的队列。线程需要协程的时候就通过P去获取。
为什么要引入P?直接把运行协程队列放到M不行吗?
- 因为如果没有P,线程必须从全局队列中获取协程,这个过程会涉及到加锁。并发量大的情况下频繁的加锁解锁操作会影响性能。
- 有了P,一个线程阻塞的时候可以把它对应的P上的其他协程转移到其他线程。
Go调度的核心思想是什么?
- 重用线程
- 限制同时运行的线程数等于CPU核数
- 线程私有runqueues,可以从其他线程获得协程(工作窃取),也可以在阻塞时将runqueues给其他线程
goroutine的调度时机有哪些?
- 创建新的协程
- 垃圾回收
- 系统调用
- 内存同步访问
感觉涉及到具体的调度策略时更多和操作系统知识相关,而与具体语言无关,贴一大段代码也没啥意思,这里就不再赘述了。