Я новичок в Go и слежу за gobyexample.com, чтобы изучить основы. Когда я дохожу до раздела «закрытие каналов», учебник вставляет этот фрагмент кода (я удалю исходные комментарии):
package main
import "fmt"
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j := 1; j <= 18; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
<-done
}
Исходный код устанавливал 3 вместо 18 в цикле отправителя заданий.
Выполнение этого кода в play.golang.org - это то, что я не совсем понимаю. Он всегда выводит следующее:
sent job 1
sent job 2
sent job 3
sent job 4
sent job 5
received job 1
received job 2
received job 3
received job 4
received job 5
received job 6
sent job 6
sent job 7
sent job 8
sent job 9
sent job 10
sent job 11
sent job 12
received job 7
received job 8
received job 9
received job 10
received job 11
received job 12
received job 13
sent job 13
sent job 14
sent job 15
sent job 16
sent job 17
sent job 18
sent all jobs
received job 14
received job 15
received job 16
received job 17
received job 18
received all jobs
Итак, я понимаю, что «очередь» канала (я знаю, что эта терминология не самая точная, но для того, чтобы узнать себя, это то, что я понимаю о том, что такое канал) имеет размер 5, поэтому первые 10 сообщения журнала подходят для меня.
Но как сообщения 6 и 13 могут быть выведены при их получении до фактической отправки? Как может быть отправлено 7 сообщений подряд, если размер канала 5? Что я упускаю из этого?
But how can be the message 6 and 13 be output their reception prior than their actual sending?
Мы знаем только, что fmt.Println("received job", j)
было напечатано до того, как fmt.Println("sent job", j)
было напечатано, и это правдоподобно в параллельно работающем коде.
How can be 7 messages sent in a row, if the channel size is 5?
Опять же, поскольку мы точно не знаем, какое выражение выводится первым, этого можно ожидать. Очередь уже можно сократить, а мы еще печатаем.
Горутины выполняются одновременно, поэтому они также одновременно печатаются на стандартный вывод (т. е. не по порядку). Кроме того, API fmt.Print буферизуются, поэтому ваши отпечатки фактически не печатаются сразу после их вызова. Это происходит и в других языках.
Вот почему вы не можете получить реальное параллельное выполнение горутин из сообщений в стандартном выводе.
Функции fmt.Print записывают вывод при вызове. Функции используют буфер для построения вывода, но этот буфер записывается в стандартный вывод перед возвратом функции.
@CeriseLimón, правильно. Стандартная библиотека Go не буферизируется в os.Stdout, но ОС, вероятно, делает это. Я должен был сказать это лучше.
Буферизация не имеет ничего общего с порядком вывода, наблюдаемым OP. Что касается комментариев о других языках: Go отличается от большинства других языков тем, что функции Go fmt никогда не буферизуют вывод, записываемый непосредственно в стандартный вывод.
Немного о том, что «API fmt.Print буферизуются», — это то, что я пропустил. Спасибо большое