в golang есть такая интересная вещь, как netpoller - это пакет, который позволяет преобразовать асинхронный ввод/вывод в блокирующий
зачем это нужно?
golang заточен работать под блокирующие вызовы
например, происходит синхронное чтение из файла, горутина блокируется и ждет, пока чтение завершится
планировщик, через 10 мс выбрасывает ее вниз очереди ожидания, и берет в работу другую горутину
с синхронными вызовами все просто и понятно
а что если вызов асинхронный? в go поступили просто - умеем работать с синхронными? значит сделаем из асинхронного синхронный :)
тут в дело вступает netpoller - компонент рантайма, который умеет блокировать горутину, при этом не блокируя текущий поток выполнения
текущий поток выполнения берет в работу новую горутину из очереди, а netpoller тем временем подписывается на API операционной системы, которое работает с асинхронными вызовами
как только API выбросит событие, что ответ получен - netpoller разбудит горутину и сообщит планировщику, что ей нужно выделить процессорное время
после чего планировщик заберет горутину в один из потоков и возьмет ее на выполнение
второй интересный момент из модуля про планировщик - asynchronous preempation
это механизм, добавленный в go 1.14, позволяющий вытеснить горутину, работающую дольше 10 мс, и дать возможность поработать другим горутинам из локальной очереди
да, до версии 1.14 в случае, если мы работали на одном ядре и запускали там горутину с бесконечным циклом - поток блокировался до явного вызова runtime.Gosched()
более подробно, если тебе интересно, про это можно почитать тут - https://habr.com/ru/post/502506/
Post #264
1.94K