goroutine 调度器
对golang的goroutine的内部实现感觉非常神秘,通过这段时间的学习基本了解了golang调度器实现原理。
golang调度器
虽然golang的最小调度单元为协程(goroutine),但是操作系统最小的调度单元依然还是线程,所以golang scheduler(golang调度器)其要做的工作是如何将众多的goroutine放在有限的线程上进行高效而公平的调度。
操作系统的调度不失为高效和公平,比如CFS调度算法,那么go为何要引入goroutine?原因很多,有人会说goroutine 相比于linux的pthread机制使用很方便。但是核心原因为goroutine的轻量级,无论是从进程到线程,还是从线程到协程,其核心都是为了使得我们的调度单元更加轻量级,我们可以轻易得创建几万几十万的goroutine而不用担心内存耗尽等问题。golang引入goroutine试图在语言内核层做到足够高性能得同时(充分利用多核优势、使用epoll高效处理网络/IO、实现垃圾回收等机制)尽量简化编程。
ps:人类社会的发展是生产工具不断发展解放生产力的过程,语言的发展也是一样,从机器语言到汇编语言、C语言、面向对象C++\ Java以及到现在层出不穷的动态语言。编程语言作为生产工具,其发展核心目的为最大化IT工作人员的生产力。从这点看,我很看好go语言,极简得编程之道简直大爱。
以下基于Daniel Morsing的一篇文章介绍goroutine调度器。
首先基于线程,用户态协程可以选择以下3种调度机制。
N:1 即多个用户态协程运行在一个os线程上,这种方式的优点是可以很快得进行上下文切换,但是缺点是不能利用多核优势。
1:1 一个用户态协程对应一个os线程,这种方式得优点是可以利用到多核的优势,但是协程的调度完全依赖于os线程的调度,而os线程的调度的上线文切换的代价又比较大,从而导致这种模型调度的上下文切换代价比较大。
M:N golang scheduler 使用的m:n调度模型,即任意数量的用户态协程可以运行在任意数量的os线程上,这样不仅可以使得上线文切换更加轻量级,同时又可以充分利用多核优势。 为了实现这种调度机制,golang 引入如下3个大结构