Go language comes with an efficient coroutine scheduler, which can easily handle concurrent tasks and implement high-performance programs. In this article, we will take an in-depth look at the coroutine scheduler in the Go language and explore its implementation, operation, and optimization.
Introduction to coroutines
Coroutines are lightweight threads or user-mode threads. It is scheduled by the programmer, not by the operating system. The characteristic of coroutines is that they are non-preemptive, that is, the context is switched only when the yield() function is explicitly called. Therefore, the switching overhead of coroutines is very small, they can be easily created and destroyed, and many coroutines can be run at the same time to achieve concurrent execution of programs.
Go language coroutine model
Go language adopts the M:N coroutine model, that is, multiple user-mode threads M, corresponding to the relationship of multiple system-level threads N. This model takes full advantage of multi-core CPUs, while reducing context switching overhead and improving scheduling performance.
M represents the operating system thread, that is, the physical thread, which is the smallest unit of operating system scheduling. N represents the virtual thread (goroutine) in the Go language runtime system (runtime), which is the smallest unit to achieve concurrency. N goroutines will be mapped to M threads and scheduled by the scheduler at runtime.
Coroutine Scheduler
The coroutine scheduler is the core component in the Go language runtime system and is responsible for managing and scheduling multiple coroutines to execute tasks. It is a high-level scheduler that can control the running and switching of coroutines and realize level scheduling of coroutines. In the Go language, the coroutine scheduler uses an additional running entity called Goroutine, which can switch execution tasks in the coroutine more efficiently.
Coroutine scheduler implementation principle
The coroutine scheduler implementation principle can be divided into two levels: the operating system level and the Go language runtime system level.
Operating system level
On the operating system level, the coroutine scheduler will map multiple user threads to multiple operating system threads at runtime, taking advantage of the parallel computing capabilities of multi-core CPUs.
The M:N architecture in the Go language coroutine model, that is, M represents the operating system thread (Machine), N represents the virtual thread of the Go language (N, represents Goroutine), and is managed by the scheduler at runtime. . The main function of the scheduler is to maintain a scheduling task queue on each operating system thread, dynamically schedule the execution of tasks on each thread according to the priority of the tasks in the task queue and the scheduling algorithm, and manage thread resources.
Go language runtime system level
At the Go language runtime system level, the coroutine scheduler uses three mechanisms: scheduler, scheduler queue and P.
Scheduler
The coroutine scheduler of Go language is controlled by a global scheduler, which maintains the scheduler queue, P queue, spin number, scheduling algorithm, etc. The scheduler dynamically manages task execution on each thread to optimize the execution efficiency of the coroutine.
Scheduler Queue
The scheduler queue is where the scheduler records all Goroutines waiting to be scheduled. When the scheduler allocates Goroutine to P, it will first look for Goroutine waiting for scheduling from the queue. If found, it will immediately put them into P's local queue. If not found, it will create a new Goroutine and put it in in P's local queue.
P
P is a processor used to execute Goroutine, and the queue it owns is the local queue. The number of P is controlled by the GOMAXPROCS environment variable. If not set, the Go program defaults to the number of cores of the machine.
Optimization of Go scheduler
Go scheduler has many optimization strategies, the following are a few of them:
When all Goroutines in a certain P queue are blocked, the Go scheduler will search for Goroutines in other P queues and steal some and put them in its own queue for running. This strategy ensures stable load and load sharing for all Ps.
Goroutine can set a timer for itself during execution. When the time is up, call runtime.Goexit to tell the runtime that this Goroutine can be actively scheduled. The Go scheduler also has a pre-preemption policy if the goroutine does not actively call Goexit. Prior to Go 1.13, preemption was only possible when GOMAXPROCS was 1. It has now been upgraded to support more situations.
In order to reduce the competition for tasks between different threads, each thread will have its own local queue, and each thread will take priority from the local queue. Get the task and execute it. Only when the local queue is empty, the task will be fetched from the global queue.
Summary
The coroutine scheduler is one of the key components of high-performance concurrency in the Go language. It is responsible for managing and scheduling multiple coroutines to execute tasks. It adopts the M:N coroutine model and uses different mechanisms and algorithms at the operating system level and Go language runtime system level to ensure efficient execution and scheduling of coroutines. At the same time, the Go scheduler also has many optimization strategies, such as coroutine preemption, local run queues, and task stealing, which can help the program run more efficiently and improve program performance and throughput.
The above is the detailed content of Detailed explanation of coroutine scheduler in Go language. For more information, please follow other related articles on the PHP Chinese website!