Go语言学习日志之channel引发死锁问题

在学习Go语言channel时遇到死锁问题。通过分析发现,无缓存channel的写操作会阻塞,直到有读操作,而main函数协程在写后阻塞等待读,造成死锁。将读写操作放入不同协程解决问题。但使用有缓存channel仍可能出现死锁,需在读取前关闭channel以避免阻塞。
摘要由CSDN通过智能技术生成

今天在看到Go中的channel时,就自己动手试了一下这个数据结构,先贴原始代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch :=make(chan string)
	ch <-"abc"
	ch <-"def"
	for {
		fmt.Println(<-ch)
	}
}

乍一看没毛病,但就是这么简单的一个测试运行就直接报错了:

D:\Go\bin\go.exe build -o C:\Users\wangxihe\AppData\Local\Temp\___go_build_ChannelTest_go.exe D:/WorkSpace/Go/GoLearning/ChannelTest.go #gosetup
C:\Users\wangxihe\AppData\Local\Temp\___go_build_ChannelTest_go.exe #gosetup
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        D:/WorkSpace/Go/GoLearning/ChannelTest.go:12 +0x65

Process finished with exit code 2

这就往里面写了两个字符串,然后再读出来,怎么就死锁了呢?
然后我就查到了这位大佬的资料,先贴上链接以示尊敬:https://blog.csdn.net/u011328417/article/details/89473323
下面附上我自己综合别的资料的理解:
首先,使用ch :=make(chan string)创建的channel是无缓存的,这就意味着<-ch这句会被阻塞直到接收到数据才被执行。并且当使用ch <-"abc"进行写的时候,协程也会被阻塞直到有语句对这个channel进行读操作,而main函数的执行在go语言中本身就是一个协程的执行,所以在执行到c<-'A’的时候,执行main函数的协程将被阻塞,进而fmt.Println(<-ch)也将被阻塞无法执行,所以发生了死锁。
根据这位大佬的方法,将读和写操作写在不同的协程中,修改代码如下:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch :=make(chan string)
	go test(ch)
	go print(ch)
	time.Sleep(time.Millisecond)
}

func test(ch chan string)  {
	ch <-"abc"
	ch <-"def"
}

func print(ch chan string)  {
	for {
		fmt.Println(<-ch)
	}
}

问题解决,不报错而且正常输出

GOROOT=D:\Go #gosetup
GOPATH=C:\Users\wangxihe\go #gosetup
D:\Go\bin\go.exe build -o C:\Users\wangxihe\AppData\Local\Temp\___go_build_ChannelTest_go.exe D:/WorkSpace/Go/GoLearning/ChannelTest.go #gosetup
C:\Users\wangxihe\AppData\Local\Temp\___go_build_ChannelTest_go.exe #gosetup
abc
def

Process finished with exit code 0

综上所述,这一切的问题好像都是没有缓存引起的,那么理论上来说,如果使用有缓存的channel是否就可以解决了呢?
附上更改后的代码:

package main

import "fmt"

func main() {
	ch :=make(chan string,100)
	ch <-"abc"
	ch <-"def"
	for {
		fmt.Println(<-ch)
	}
}

运行发现可以输出结果但还是会报死锁的错:

GOROOT=D:\Go #gosetup
GOPATH=C:\Users\wangxihe\go #gosetup
D:\Go\bin\go.exe build -o C:\Users\wangxihe\AppData\Local\Temp\___go_build_cTest_go.exe D:/WorkSpace/Go/GoLearning/cTest.go #gosetup
C:\Users\wangxihe\AppData\Local\Temp\___go_build_cTest_go.exe #gosetup
abc
def
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        D:/WorkSpace/Go/GoLearning/cTest.go:10 +0x9f

Process finished with exit code 2

分析了一下,发现其实还是同样的问题,fmt.Println(<-ch)又被阻塞了,于是就在读取之前先把channel关闭,代码如下:

package main

import (
	"fmt"
)

func main() {
	ch :=make(chan string,100)
	ch <-"abc"
	ch <-"def"
	close(ch)
	for i := range ch {
		fmt.Println(i)
	}
}

再次运行,问题解决:

GOROOT=D:\Go #gosetup
GOPATH=C:\Users\wangxihe\go #gosetup
D:\Go\bin\go.exe build -o C:\Users\wangxihe\AppData\Local\Temp\___go_build_cTest_go.exe D:/WorkSpace/Go/GoLearning/cTest.go #gosetup
C:\Users\wangxihe\AppData\Local\Temp\___go_build_cTest_go.exe #gosetup
abc
def

Process finished with exit code 0

大功告成,如果有哪里说的不对的欢迎各位大佬留言或者私信指教!
留下本菜鸟的邮箱:2681465985@qq.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值