Почему эта программа с двумя worker занимает ~6с, а не ~3с?
Предскажите напечатанное число и объясните тайминг.
func worker() chan int {
ch := make(chan int)
go func() {
time.Sleep(3 * time.Second)
ch <- 1
}()
return ch
}
func main() {
start := time.Now()
_, _ = <-worker(), <-worker()
println(int(time.Since(start).Seconds()))
}
Объясните, почему не ~3с, и как сделать ~3с.
Печатает 6. Go вычисляет выражение с двумя операндами слева направо: первый <-worker() вызывает worker() (запускает goroutine №1) и затем блокируется на 3с на приёме из него. Только после этого вообще стартует второй worker(), и блокируется ещё на 3с — они идут последовательно, всего ~6с. Чтобы совместить, сначала запустите обе: c1, c2 := worker(), worker(); <-c1; <-c2 → ~3с.
- ✗Считать, что Go запускает оба вызова
worker()до любого приёма - ✗Думать, что отбрасывание через
_пропускает блокирующий приём - ✗Полагать, что совместить работу может только буфер, а не перестановка запусков
- →Каков в Go порядок вычисления операндов многозначного выражения?
- →Изменит ли буферизация channel результат ~6с, и почему да или нет?
Что выведет
6.
Выражение <-worker(), <-worker() вычисляется слева направо:
- Вычисляется левый операнд
<-worker(): вызываетсяworker()— стартует goroutine №1 (она спит 3с), возвращается её channel, иmainблокируется на приёме из него. Через 3с приём завершается. - Только теперь вычисляется правый операнд: вызывается второй
worker()— лишь сейчас стартует goroutine №2, иmainблокируется ещё на 3с.
Итого ~6 секунд. Goroutine №2 даже не существует, пока не отработал первый приём.
Как сделать ~3с
Запустите обе goroutine до приёмов, чтобы их сны перекрылись:
c1, c2 := worker(), worker() // обе goroutine стартуют сразу
<-c1
<-c2 // обе уже спали параллельно → ~3с
Буферизация channel сама по себе проблему не решает: дело в порядке вычисления, а не в блокировке отправки.