Каналы make(chan int)
имеет неявный размер ноль (ref: https://golang.org/ref/spec#Making_slices_maps_and_channels)
Канал нулевого размера не буферизован. Канал указанного размера make (chan int, n) буферизуется. См. http://golang.org/ref/spec#Send_statements для обсуждения каналов с буферизацией и без буферизации. Пример на http://play.golang.org/p/VZAiN1V8-P иллюстрирует разницу.
Здесь канал <-ch
или ch <-
будет заблокирован, пока кто-нибудь не обработает его ( одновременно ). Если вы попробуете эту программу ручкой и бумагой, вы поймете, почему она заблокирована. На рисунке ниже показан возможный поток данных по каналу ch
:
Так что если вы сделаете ваш ch := make(chan int64)
до ch := make(chan int64,1)
, он будет работать.
var counter int64 // shared resource
var wg sync.WaitGroup
func main() {
ch := make(chan int64, 1)
wg.Add(2)
go incCounter(ch)
go incCounter(ch)
ch <- counter
wg.Wait()
fmt.Println("Final Counter:", counter) // expected value is 4
}
func incCounter(ch chan int64) {
defer wg.Done()
for count := 0; count < 2; count++ {
value := <-ch
value++
counter = value
ch <- counter
}
}
Если мы проанализируем, как работает программа, когда вы используете ch := make(chan int64)
, мы увидим, что одна подпрограмма go заблокирована в этой программе (другая закрыта). С помощью time.Sleep(n)
и получения последних данных из канала в процедуре заблокированного перехода мы можем преодолеть тупик. Смотрите код ниже:
var counter int64 // shared resource
var wg sync.WaitGroup
func main() {
ch := make(chan int64)
wg.Add(2)
go incCounter(ch)
go incCounter(ch)
ch <- counter
// to ensure one go routine 'incCounter' is completed and one go routine is blocked for unbuffered channel
time.Sleep(3*time.Second)
<-ch // to unblock the last go routine
wg.Wait()
fmt.Println("Final Counter:", counter) // expected value is 4
}
func incCounter(ch chan int64) {
defer wg.Done()
for count := 0; count < 2; count++ {
value := <-ch
value++
counter = value
ch <- counter
}
}