go - 关于使用 channel 时遇到的死锁问题
2017-10-19 11:47
274 查看
1,发生死锁的代码
func deadlockTest() { ch := make(chan int) results := make(chan int) for i := 0; i < 2; i++ { go func() { // 把从channel里取得的数据,再传回去 x := <-ch results <- x }() } // 向输入数据里传两个数据 ch <- 1 ch <- 2 for re := range results { fmt.Printf("re:%v\n", re) } }
结果:
re:1 re:2 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan receive]: main.deadlockTest() /Users/iss/goproj/go_bible/src/main.go:45 +0x174 main.main() /Users/iss/goproj/go_bible/src/main.go:14 +0x20 Process finished with exit code 0
2,发生死锁的原因
主程序中的for re := range results代码,在处理完
1和
2两条数据后,还一直在等待 results channel 的内容,无法结束。这样的话,Go就会判定产生了死锁
for re := range results { fmt.Printf("re:%v\n", re) }
3,如果解决这个死锁问题
方案1:
代码
func deadlockTestOKVer1() {
ch := make(chan int)
results := make(chan int)
for i := 0; i < 2; i++ {
go func() {
// 把从channel里取得的数据,再传回去
x := <-ch
results <- x
}()
}
// 向输入数据里传两个数据
ch <- 1
ch <- 2
go func() {
for re := range results { fmt.Printf("re:%v\n", re) }
}()
// 等待3秒后,才结束主程序。
// 如果不加等待的话,上面的 go func 可能还没有执行,程序就结束了,go func 跟着结束了。
time.Sleep(3 * time.Second)
}
解析:
1,这个方法是把 results channel 的接收处理,放到一个goroutine里去做。这样的话,主程序就不会卡住不动,即产生死锁了。2,然后,在最后一行加了一个等待(time.Sleep(3 * time.Second))。如果不加等待的话,上面的 go func 可能还没有执行,程序就结束了,go func 跟着结束了。(和Sleep相比,还有更好的方法,比如使用WaitGroup)
(fatal error: all goroutines are asleep - deadlock!)这种抛出 error 的死锁,是只有主程序卡住不动才会生产的吗?如果是的话,那不让主程序卡住不动,就不会发生这种事情了。所以以下的各种解决方案,都是围绕如何“不让主程序卡住”来做的。
方法2:
代码
func deadlockTestOKVer2() {
var wg sync.WaitGroup
ch := make(chan int)
results := make(chan int)
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
// 把从channel里取得的数据,再传回去
x := <-ch
results <- x
defer wg.Done()
}()
}
// 在(for i := 0; i < 2; i++)循环里的 go func 都执行完后,
// 关闭 result channel,这样主程序里的 for reuslts 循环 就可以正常结束了。
go func() {
wg.Wait()
close(results)
}()
// 向输入数据里传两个数据
ch <- 1
ch <- 2
for re := range results { fmt.Printf("re:%v\n", re) }
// 等待3秒后,才结束主程序。
// 如果不加等待的话,上面的 go func 可能还没有执行,程序就结束了,go func 跟着结束了。
time.Sleep(3 * time.Second)
}
解析
最上面死锁的原因是,for result循环一直在等待输入,但实际上输入只有2个,处理完后就没有了。为了解决这个问题,把 results channel 关闭,
for result循环就可以正常退出了。
具体细节是,在所有的 results channel 的输入处理之前,
wg.Wait()这个goroutine会处于等待状态。当处理完后(wg.Done),
wg.Wait()就会放开执行,执行后面的
close(results)。
注意:要把
wg.Add(1)放到
go func外面。如果放到里面的话,(for i := 0; i < 2; i++)里的
go func有可能会在
wg.wait的
go func之后执行,这样
close(results)就会先执行。
4,什么样的情况会死锁?
总结一下,channel 上如果发生了流入和流出不配对,就可能会发生死锁。
例子:
(1)无缓冲区死锁 func deadlockTest1() { ch := make(chan int) ch <- 0 } (2)有缓冲区死锁 func deadlockTest1() { ch := make(chan int, 1) ch <- 0 <- ch <- ch }
如何不发生死锁
1,明确保证流入和流出成对出现2,当无法保证流入和流出成对出现时(例如上面的 for range),在流入完成后,关闭 channel。(上面的方法2)
3,利用缓冲。明确保证流入成对出现的话,可以使用缓冲来解决
流入的阻塞。
func main() { ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3 for v := range ch { fmt.Println(v) } }
参考
Go语言并发与并行学习笔记(一)相关文章推荐
- 刚才遇到了关于C#使用外部DLL函数上的char*的问题。
- Nagios遇到的一点问题--关于如何使用timeperiod
- Reporting Service Tips 101(#3) - 关于使用Sum函数会遇到的问题(2)
- 关于SQL2005使用中遇到的问题及解答
- 关于ibatis使用中遇到的问题 和解决方案
- 关于用c生成的dll在使用其他供应商的工具创建可执行模块时遇到的一些问题
- 关于使用contacts 或者 contacts_cn 遇到的问题解决
- 关于使用easyui dataGrid遇到的小bug问题
- 关于 VS2010 使用中遇到的一点小问题
- 关于在使用ofstream类中遇到的问题(VS2010)
- 关于ActivityGroup使用过程中遇到的一点问题
- 关于您提到使用WebServices时候遇到“基础连接以关闭”的问题
- Reporting Service Tips 101(#2) - 关于使用Sum函数会遇到的问题(1)
- 关于ActivityGroup使用过程中遇到的一点问题
- 解决:关于 SHFILEOPSTRUCT 的使用遇到的问题
- 关于 SHFILEOPSTRUCT 的使用遇到的问题
- 关于ActivityGroup使用过程中遇到的一点问题
- 关于我使用spring mvc框架做文件上传时遇到的问题
- 关于IIS在使用中遇到的一些问题的总结
- 关于在UTF-8编码下使用ASPUPLOAD组件时遇到的问题