Ответ 1
Обновление (Go версии 1.2 +)
Начиная с Go 1.2, планировщик работает по принципу упреждающей многозадачности. Это означает, что проблема в исходном вопросе (и нижеприведенное решение) больше не актуальна.
Из примечаний Go 1.2
Предотвращение в планировщике
В предыдущих выпусках, горутин, который зацикливался навсегда, мог бы изгонять других гортанов в том же потоке, серьезная проблема, когда GOMAXPROCS предоставил только один пользовательский поток. В Go > 1.2 это частично исправлено: планировщик иногда ссылается на вход в функцию. Это означает, что любой цикл, который включает (не вложенную) функцию вызов может быть предварительно упущен, что позволяет другим гортанам работать в одном потоке.
Короткий ответ
Он не блокирует записи. Он застрял в бесконечном цикле processevents
.
Этот цикл никогда не уступает планировщику, заставляя все goroutines блокироваться неограниченно.
Если вы закомментируете вызов processevents
, вы получите результаты, как ожидалось, вплоть до 100-й записи. В этот момент программа панически, потому что никто не читает с канала.
Другим решением является вызов runtime.Gosched()
в цикле.
Длинный ответ
С Go1.0.2 Планировщик Go работает по принципу Совместная многозадачность. Это означает, что он выделяет процессорное время для различных goroutines, работающих в заданном потоке операционной системы, поскольку эти подпрограммы взаимодействуют с планировщиком в определенных условиях. Эти "взаимодействия" возникают, когда определенные типы кода выполняются в goroutine. В данном случае это предполагает выполнение каких-либо операций ввода-вывода, системных вызовов или распределения памяти (в определенных условиях).
В случае пустой петли такие условия никогда не встречаются. Поэтому планировщику никогда не разрешено запускать свои алгоритмы планирования до тех пор, пока этот цикл работает. Это, следовательно, не позволяет ему выделять процессорное время другим гортанам, ожидающим запуска, и результат, который вы наблюдаете, наступает: вы эффективно создали тупик, который не может быть обнаружен или разбит планировщиком.
Пустая петля обычно никогда не нужна в Go и в большинстве случаев укажет на ошибку в программе. Если вам это нужно по какой-либо причине, вы должны вручную перейти к планировщику, вызвав runtime.Gosched()
на каждой итерации.
for {
runtime.Gosched()
}
Значение GOMAXPROCS
для значения > 1
было упомянуто как решение. Хотя это избавит вас от непосредственной проблемы, которую вы заметили, она эффективно переместит проблему в другой поток ОС, если планировщик решит переместить цикл goroutine в свой собственный поток ОС. Это не гарантирует, если вы не назовете runtime.LockOSThread()
в начале функции processevents
. Даже тогда я все равно не буду полагаться на этот подход, чтобы быть хорошим решением. Просто вызывая runtime.Gosched()
в самом цикле, решит все проблемы, независимо от того, какой поток ОС работает в goroutine.