【Go学习】高级特性(一)调度机制

105 阅读2分钟

线程与协程的区别是什么?

协程是线程的再细分。

接下来会从三个方面解答这一问题:内存消耗、创建和销毁、切换

内存消耗

创建一个线程需要1MB栈内存,而一个协程仅需2KB。

创建和销毁

在操作系统中,有用户态和内核态两个概念。线程有内核级的线程和用户级的线程,但是最终还是涉及到了内核级。而协程Go的runtime负责管理,是用户级的。

切换

线程切换要保存各类寄存器,而协程只需要看三个:

  • 程序计数器
  • 栈指针
  • 基址指针

关于Go的调度器

了解调度所需的三个结构体吗?

  • G;协程
  • M:线程
  • P:虚拟的处理器
  • sched:维持调度器的运行

大致讲讲这个过程?

runtime起始时会启动一些协程,并通过启动线程来开始协程的运行;协程越多,对应的承载它的线程也越多(M-N模型);每个虚拟处理器维护一个出于运行状态的协程的队列。线程需要协程的时候就通过P去获取。

为什么要引入P?直接把运行协程队列放到M不行吗?

  • 因为如果没有P,线程必须从全局队列中获取协程,这个过程会涉及到加锁。并发量大的情况下频繁的加锁解锁操作会影响性能。
  • 有了P,一个线程阻塞的时候可以把它对应的P上的其他协程转移到其他线程。

Go调度的核心思想是什么?

  • 重用线程
  • 限制同时运行的线程数等于CPU核数
  • 线程私有runqueues,可以从其他线程获得协程(工作窃取),也可以在阻塞时将runqueues给其他线程

goroutine的调度时机有哪些?

  • 创建新的协程
  • 垃圾回收
  • 系统调用
  • 内存同步访问

感觉涉及到具体的调度策略时更多和操作系统知识相关,而与具体语言无关,贴一大段代码也没啥意思,这里就不再赘述了。