Блокирующий syscall
Под нагрузкой экспорт архивов встал: тысячи горутин одновременно ушли в чтение с диска и повисли. Разбор показал, что дисковые чтения держат потоки по-настоящему, в отличие от сетевых.
когда горутина уходит в блокирующий системный вызов, P отвязывается от её потока и продолжает исполнять другие горутины — но массовый блокирующий I/O всё равно может исчерпать потоки.
Что происходит с M и P
Когда горутина делает блокирующий syscall — например, читает файл с диска, — её поток M уходит в ядро и не может исполнять никакой Go-код, пока не вернётся. Планировщик в этой точке отвязывает P от M: P остаётся в работе, его захватывает другой M (создаётся новый, если свободных нет), и очередь продолжает исполняться. Сам M висит в syscall со своей горутиной.
// каждая горутина: честный блокирующий вызов — файл, не сокет
data, err := os.ReadFile(path)
Цена блокирующих вызовов
Парковка из прошлых уроков — «бесплатная»: горутина спит, поток свободен. Блокирующий syscall — платный: поток реально занят ядром. Пока горутина ждёт диск, один M исключён из игры.
Thread exhaustion
Если тысячи горутин одновременно уходят в блокирующие syscall'ы, runtime заводит новые M. Но потоки не бесплатны, и есть предел: при массовом блокирующем I/O можно упереться в исчерпание потоков — производительность деградирует, хотя горутин «всего лишь» тысячи. Дисковый I/O под нагрузкой обычно и ограничивают — об этом урок.
Вы узнаете:
- как sysmon следит за M, зависшими в syscall
- чем «диск» отличается от «сети»
Проверьте себя
Горутина ушла в блокирующий syscall. Что происходит с её P и M?
Тысячи запросов одновременно уходят в дисковые чтения — памяти хватает, но производительность падает. Почему?
Как защититься от исчерпания потоков при блокирующем I/O?
правильных ответов
Блокирующий syscall: P спасают, отвязывая от M, но сам поток занят ядром. Массовый блокирующий I/O — отдельная забота, не «бесплатная», как парковка.
10 000 ₽ · 60 минут · онлайн