Ответ
Блокирующая операция — это любая операция, которая приостанавливает выполнение текущей горутины до тех пор, пока не будет выполнено определенное условие.
Основные примеры блокирующих операций:
Операции с каналами:
- Отправка в небуферизованный или заполненный буферизованный канал (
ch <- val
). - Чтение из пустого канала (
val := <-ch
).
- Отправка в небуферизованный или заполненный буферизованный канал (
Сетевой ввод-вывод:
- Чтение из сокета (
conn.Read()
). - Запись в сокет, если буфер переполнен (
conn.Write()
).
- Чтение из сокета (
Синхронизация:
- Захват мьютекса (
mutex.Lock()
), если он уже занят. - Ожидание
sync.WaitGroup
(wg.Wait()
).
- Захват мьютекса (
Системные вызовы:
- Чтение из файла (
file.Read()
). - Вызов
time.Sleep()
.
- Чтение из файла (
Как рантайм Go обрабатывает блокировки?
Это одна из ключевых особенностей Go. Когда горутина выполняет блокирующую операцию (например, сетевое чтение), планировщик Go не блокирует весь системный поток (OS thread).
Вместо этого он:
- Паркует блокированную горутину (переводит её в состояние ожидания).
- Снимает её с текущего системного потока
M
. - Берет другую готовую к выполнению горутину из очереди и запускает её на этом же потоке
M
.
Когда блокирующая операция завершается (например, пришли данные из сети), сетевой поллер (netpoller) уведомляет планировщик, и горутина снова помещается в очередь готовых к выполнению.
Это позволяет эффективно утилизировать процессорное время и поддерживать сотни тысяч горутин на небольшом количестве системных потоков.
Пример блокировки на канале:
func main() {
ch := make(chan string) // Небуферизованный канал
go func() {
fmt.Println("Горутина: жду 2 секунды...")
time.Sleep(2 * time.Second) // Блокировка на 2с
ch <- "привет из горутины" // Блокировка, пока main не будет готова читать
}()
fmt.Println("Main: жду данные из канала...")
message := <-ch // Блокировка, пока горутина не отправит данные
fmt.Printf("Main: получил '%s'n", message)
}
Для контроля над блокирующими операциями и предотвращения вечных блокировок (deadlock) активно используется пакет context
с таймаутами или отменой.