我写了一些旨在使用频道进行同步的代码。
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
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
}
}
当我运行该程序时,发生了错误:all goroutines are asleep - deadlock!
。但是我无法解决问题,也不知道出了什么问题。有人可以帮忙吗?
答案 0 :(得分:8)
频道make(chan int)
的隐式大小为零(参考: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)
时程序的工作方式,我们可以看到该程序阻塞了一个执行例程(退出了另一个例程)。借助time.Sleep(n)
并在阻塞的go例程中从通道接收最后的数据,我们可以克服死锁。请参见下面的代码:
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
}
}