Ответ
Горутина переходит в состояние ожидания (блокируется), когда не может продолжить выполнение и ожидает определенного события. Планировщик Go в этот момент переключается на другую готовую к выполнению горутину.
Основные причины блокировки:
Операции с каналами:
- Чтение из пустого канала:
val := <-ch
. - Запись в заполненный буферизированный или в небуферизированный канал:
ch <- val
.
- Чтение из пустого канала:
Примитивы синхронизации (пакет
sync
):- Захват заблокированного мьютекса:
mu.Lock()
. - Ожидание
WaitGroup
:wg.Wait()
. - Вызов
Cond.Wait()
.
- Захват заблокированного мьютекса:
Сетевые и файловые I/O-операции. Большинство системных вызовов, связанных с вводом-выводом, интегрированы с планировщиком Go. Горутина "засыпает" до получения данных из сети или файла.
Таймеры и
time.Sleep
. Вызовtime.Sleep(duration)
переводит текущую горутину в режим ожидания на указанное время.Оператор
select
. Если ни один изcase
вselect
не готов к выполнению и отсутствует веткаdefault
, вся конструкцияselect
блокирует горутину до тех пор, пока одна из операций не станет доступной.Вызов
runtime.Gosched()
. Это добровольная уступка процессорного времени. Горутина не блокируется в ожидании события, а просто позволяет планировщику запустить другие горутины, после чего вернется в очередь готовых к выполнению.
Важно понимать, что в большинстве этих случаев блокируется только сама горутина, а не поток операционной системы (OS thread). Планировщик Go эффективно использует потоки, переключая на них другие, не заблокированные горутины.