协程
概念
协程是轻量级的线程,创建成本很低,与线程不同的是其不受操作系统调度,协程的调度由用户程序提供,go语言中的协程调度器将协程调度到线程中运行,用户使用go关键字即可创建协程。
与线程相比的优势
? 频繁的创建线程会造成不必要的开销,因此引入了线程池的技术,线程池中预先包存一定数量的线程,新任务将不在创建线程,而是将任务放在线程池的任务队列中,线程池中的线程不断地从任务队列中取出任务并执行,这样可以有效减少频繁创建任务造成的开销。系统调用会使线程池的状态恶化,如果大部分线程进入阻塞状态,会使得任务队列中的任务产生堆积,因此就需要创建更多的线程,而过多的线程会使得上下文切换的开销变大,工作在用户态的协程能大大减小上下文的切换开销!
? 协程调度器可以把多个协程调度到线程中运行,同时即使把阻塞的协程调度出来,有效避免了线程的频繁切换,达到了使用少量线程实现高并发的效果。
调度模型G、M、P
- G:协程,每个go关键字都会创建一个新的协程
- M:工作线程,由操作系统调度
- P:处理器(由go定义的一个概念,并不是CPU)包含了运行GO代码的必要资源,拥有调度gorouting的能力
M必须持有P才能执行代码,M和其它的线程一样也会被操作系统阻塞
调度策略
- 队列轮转:每个处理器P维护者一个协程G的队列,处理器P依次将协程调度到M中执行。
- 系统调用:当一个协程发生系统调用后,对应的M将释放P,某个冗余的M将获得P的控制权,继续执行P队列剩下的G,当协程结束系统调用后,如果有空闲的P则获取一个P继续执行,如果没有空闲的P则将其放入全局队列中,等待被其它的P调度。
- 工作量窃取:当某个处理器P没有需要调度的协程时,将从其它处理器中偷取协程
- 抢占式调度:避免某个协程长时间运行,从而阻碍其它协程被调度的机制。