Я не уверен, что действительно понимаю, чего вы хотели достичь, особенно по причине этого странного l oop:
for i := 0; i < 4; i++ {
num := <- c
fmt.Println(num * num)
}
Но в любом случае. Прежде всего, несколько объяснений того, как работают каналы и горутины.
Канал - это потокобезопасный канал обмена сообщениями, используемый для обмена данными в разных контекстах выполнения. Канал создается с помощью инструкции make
, тогда
c := make(chan int, 3)
означает создание канала типа int с размером «буфера» 3. Этот элемент очень важен для понимания. Канал действительно следует шаблону производитель / потребитель с этими базовыми правилами:
Для производителя:
- если я попытаюсь передать sh некоторые данные в канале с некоторым «свободным пространством» , он не блокируется, и выполняются следующие инструкции
- Если я пытаюсь передать sh некоторые данные в канале без «свободного места», он блокируется до тех пор, пока не будет обработана предыдущая
Для потребителя:
- Если я пытаюсь взять элемент из пустого канала, он блокируется до тех пор, пока не появятся некоторые данные
- Если я пытаюсь взять элемент из непустого канала, он возьмите первый (канал - это FIFO)
Обратите внимание, что все операции блокировки могут быть отключены с помощью некоторого специального шаблона, доступного с помощью инструкции select
, но это уже другая тема :).
Горутины - это легкие подпроцессы подпрограммы (без потоков). Здесь нет места для подробного объяснения всех деталей, но важно то, что 1 горутина === 1 стек выполнения.
Итак, в вашем случае результат вполне предсказуем, пока не останется только один потребитель. После того, как вы запустите вторую горутину, порядок потребления по-прежнему предсказуем (размер канала только один), а порядок выполнения - нет!
Примечательно одно: у вас всего 7 выходов ... потому что ваша основная горутина завершается до того, как это произойдет, завершая выполнение всей программы. Этот момент объясняет, почему у вас не было all goroutines are asleep - deadlock!
.
Если вы хотите его иметь, вы просто должны добавить <- c
где-нибудь в конце основного:
package main
import (
"fmt"
)
func squares(c chan int) {
for i := 0; i < 4; i++ {
num := <-c
fmt.Println(num * num)
}
}
func main() {
fmt.Println("main start")
c := make(chan int)
go squares(c)
c <- 1
c <- 2
c <- 3
c <- 4
go squares(c)
c <- 5
c <- 6
c <- 7
c <- 8
<-c
fmt.Println("main stop")
}
У вас будет поведение, которое, как я думаю, вы ожидаете:
main start
1
4
9
25
36
49
64
16
fatal error: all goroutines are asleep - deadlock!
Редактировать: шаг за шагом, выполнение:
// a goroutine is created and c is empty. because
// the code of `squares` act as a consumer, the goroutine
// "block" at instruction `num := <-c`, but not the main goroutine
go squares(c)
// 1 is pushed to the channel. Till "c" is not full the main goroutine
// doesn't block but the other goroutine may be "released"
c <- 1
// if goroutine of squares has not consume 1 yet, main goroutine block
// untill so, but is released just after
c <- 2
// it continues with same logic
c <- 3
c <- 4
// till main goroutine encountered `<- c` (instruction I added) .
// Here, it behave as a consumer of "c". At this point all
// goroutine are waiting as consuler on "c" => deadlock