前面说了,go中协程有自己的调度算法,
go调度由三部分组成:
系统线程,用户协程和核心处理器。
处理器用于关联协程和线程,用户协程由处理器在线程上进行运行。
处理器的数量不会超过runtime包设置的GOMAXPROCS的大小。这个参数默认值为系统cpu数量。
一个处理器可以与多个线程关联,但同一时刻只能与一个线程建立绑定关系。
每个处理器有一个本地的协程队列,里面存储的是要运行的协程。同时全局环境也有一个全局队列里面也存储的要运行的协程。当本地队列满了,放不下新来的协程,就会取一半的协程放入全局队列。
当一个线程中的协程发生IO阻塞时,就会发生调度,这个线程中的处理器就会与这个线程解绑,带着本地队列后面的协程与其它线程建立绑定关系,继续运行后续的协程。选择下一个运行的协程会有1/61的机率从全局队列获取协程执行。发生阻塞的线程只保留与发生IO阻塞调用的协程的关系,当IO调用完毕,线程会试图去获取处理器,用来运行协程,如果获取不到,就会把协程放入全局协程队列。等待其它线程调度运行,自己进入休眠,等待被唤醒。
go中协程的执行不会主动放弃cpu,当一个线程执行一个协程时,如果这个协程没有发生系统调用或者进入睡眠(什么是系统调用文末讲解),那么在这个线程的cpu执行时间内,这个协程是会一直占用这个线程的。其它协程是得不到这个线程的执行权的。那什么时候协程会放弃cpu进入睡眠呢?就是在发生系统调用和主动进入睡眠时。这个时候这个协程后面的其它协程会由其它线程来进行调度执行,防止因为一个协程发生系统调用和主动进入睡眠时阻塞后面的协程执行。当协程系统调用完毕或睡眠结束后,重新进入数据队列等待其它线程进行调度执行它。