发布时间:2024-11-22 03:49:16
Go语言调度器采用的是一个M:N的模型,其中M代表操作系统的内核线程(Machine),N代表用户层的协程(Goroutine)。调度器通过将N个协程映射到M个内核线程上,实现并发执行。
1. G(Goroutine):G表示一个独立的协程,每个协程都包含了执行的函数、栈以及其他必要的元数据。调度器会创建和销毁协程,并负责在不同的线程之间进行切换。
2. P(Processor):P代表内核线程,它是调度器的执行单元。调度器会将多个协程分配给可用的P,由P负责协程的执行。每个P都有一个队列,用于保存待执行的协程。
3. M(Machine):M代表操作系统的内核线程,它负责创建和销毁用户层的协程。调度器会将协程绑定到M上,并将它们调度到可用的P上执行。
1. `schedule`函数:这个函数是调度器的主要入口,它负责根据当前的系统状态选择合适的M和P来执行协程。其中的一次调度过程包括找到可运行的协程和将它们分配给M。
2. `mstart`函数:这个函数表示M的执行起点,它负责从全局队列中取出待执行的协程,并将其绑定到当前的M上。具体而言,`mstart`会调用`findrunnable`函数来查找可运行的协程。
3. `findrunnable`函数:这个函数会从各个P的队列中查找可运行的协程。当一个协程完成执行或被阻塞时,它会被放入某个P的队列中待执行。
1. Work Stealing:当某个P的队列为空时,它有权限从其他的P中窃取带执行的协程。这个策略保证了协程的平衡分布,避免了某个P积压太多任务。
2. Global Queue:在全局的队列中,有一些长时间未执行的协程。当一个M没有可以执行的协程时,它会从全局队列中获取一个待执行的协程,避免浪费CPU资源。
3. Preemptive Scheduling:Go调度器是基于抢占式的调度模型。当一个协程执行时间过长或发生阻塞时,调度器会将它从当前的M上抢占,并重新分配给其他的M执行,确保协程的公平性。
1. 设置GOMAXPROCS:通过设置环境变量`GOMAXPROCS`的值,可以调整Go程序并发执行时的最大线程数。根据实际机器的CPU核数和应用的特点,合理配置`GOMAXPROCS`可以避免过多的线程竞争,提高性能。
2. 减少锁使用:在多个协程之间共享数据时,合理使用锁可以避免数据竞争。但是过多的锁使用可能会降低性能,因此需要在保证数据安全的前提下,尽量减少锁的使用。
3. 系统调用优化:调度器会在系统调用时挂起当前的M,并将其余调度给其他待运行的协程。尽量减少系统调用的频率和时间,可以减少调度器的开销和延迟,提高性能。
参考资料:
[1] Go source code - runtime/proc.go
[2] The Go Programming Language - Alan A. A. Donovan, Brian W. Kernighan
[3] Inside the Go Playground: Building Scalable Systems - Google Cloud