In this article I will try to briefly and concisely explain what gorutines are, when to use them, how they relate to system flows, and how the scheduler works.
What kind of gorutiny?
A goroutine is a function that performs competitively with other gorutins in the same address space.
Launch gorutinu very simple:
go normalFunc(args...)
The
normalFunc(args...)
function will start executing asynchronously with the code that called it.
')
Please note, gorutiny
very lightweight . Almost all expenses are the creation of a stack, which is very small, although it can grow if necessary.
How much to hang in grams?
To make it easier to navigate, consider the numbers
obtained empirically .
On average, you can count on about
4.5kb per gorutina. That is, for example, having 4Gb of RAM, you will be able to contain about
800 thousand working Gorutin. Maybe this is not enough to impress fans of Erlang, but it seems to me that the figure is very decent :)
And yet you should not mindlessly allocate a function to Gorutin wherever possible. It will bring benefits in the following cases:
- If asynchrony is needed. For example, when we work with a network, disk, database, mutex protected resource, etc.
- If the execution time of the function is large enough and you can get the gain by loading other cores.
“Big enough” is how much? Complex issue. Most likely you will have to decide depending on the specific situation. I can only say that from my experience on my gland (atom d525 64bit) it is ~ 50 μs. In general, test and develop flair;)
System threads
The following code is used in the source code (
src / pkg / runtime / proc.c ):
G (Goroutine) - Gorutina
M (Machine) - Machine
Each Machine operates in a separate thread and is capable of performing only one Gorutina at a time. The scheduler of the operating system on which the program is running switches the Machines. The number of machines running is limited by the
GOMAXPROCS
environment
GOMAXPROCS
or the
runtime.GOMAXPROCS(n int)
function. By default it is 1. It usually makes sense to make it equal to the number of cores.
Go Planner
The scheduler’s goal is to distribute ready-to-run gorutines (G) to free machines (M).
The picture and description of the scheduler are taken from Sindre Myren
RT Capabillites of Google Go .
Ready-made gorutines are executed in turn, that is, FIFO (First In, First Out). The execution of a gorutina is interrupted only when it can no longer be executed: that is, because of a
system call or the use of
synchronization objects (operations with channels, mutexes, etc.). There are no quanta of time for the work of a gorutina, after which she would return to the queue. To allow the scheduler to do this, you need to call the
runtime. Gosched ()
yourself
runtime. Gosched ()
runtime. Gosched ()
.
As soon as the function is ready for execution again, it enters the queue again.
findings
I will say right away that I was surprised by the current state of affairs. For some reason, subconsciously, I expected more “parallelism”, probably because I perceived the gorutins as lightweight system streams. As you can see, this is not quite the case.
In practice, this primarily means that sometimes it is worth using
runtime. Gosched ()
runtime. Gosched ()
, so that several long-lived gorutins do not stop the work of all others for a substantial time. On the other hand, such situations are quite rare in practice.
I also want to note that at the moment the planner is by no means perfect, which is mentioned in the documentation and the official FAQ. In the future, major improvements are planned. Personally, I first of all expect the possibility of giving priority to the gorutins.