This is the first day of my participation in the August More Text challenge

Do not communicate by sharing memory; instead, share memory by communicating. Don’t communicate as shared memory. Instead, communicate to share memory.

The Go language was built for concurrency and can easily construct tens of thousands of coroutines. The scheduling of coroutines is realized by MPG model.

  • M is a kernel thread, and goroutine runs on M. If a thread wants to run a task, it has to get P, get G from the local queue of P, and when the queue of P is empty, M will try to get some G from the global queue and put it on the local queue of P, or steal half from the local queue of other P and put it on its own queue of P. M runs G, and after G executes, M gets the next G from P, and so on.
  • P is the context in which the Goroutine is executed. It can be understood as the coroutine processor, which is used to execute the Goroutine. The Processor maintains runnable Goroutine queues that store all the Goroutines it needs to execute.
  • G, coroutine.

Threads are the entities that run goroutines, and the scheduler’s function is to assign runnable Goroutines to worker threads.

The essence of Go scheduling is to allocate a large number of Goroutines to a small number of threads for execution, and utilize multi-core parallelism to achieve stronger concurrency.

At some point, M and P are one-to-one, and P and G are one-to-many. It is bound to only one kernel thread for the lifetime of M, and the relationship between M and P and P and G is dynamically variable.

M and P will be timely combined and disconnected to ensure that G queue in P can be executed in a timely manner. For example, if G0 in the figure above is blocking M due to network I/O, THEN P will carry the remaining G into the arms of other M. (In many cases, there are more P’s than M’s,)

While one P is executing a coroutine, the others are waiting. When the executing coroutine encounters a blocking condition, such as an IO operation, the GO processor executes other coroutines.

The coroutine of GO is non-preemptive, and the coroutine proactively cedes control. That is to say, when IO operation occurs above, it is not the scheduler that forces the switch to execute other coroutines, but the scheduler executes other coroutines only after the current coroutine cedes control. Let’s list the points where Goroutine can switch:

  • IO, select
  • channel
  • Waiting for the lock
  • runtime.Gosched()

Because of wrong preemptive, hence the light structure of coroutines, if is pre-emptive, so will be in when to switch tasks, save the current context, because if the current thread is doing one thing, do it in half, we are forced to stop, then we will have to save a lot of information, avoid task error when switch back again.