package main
import ( "fmt" )
var complete chan string = make(chan string)
func printstring(i int) { fmt.Println("打印这里", i) complete <- "妥了" }
func main() {
go printstring(1)
msg := <-complete
fmt.Println(msg)
printstring(2)
}
已经有一写一读,为什么还会死锁?怎么样才能不死锁呢?
有疑问加站长微信联系(非本文作者)

select了解一哈
因为你没异步
所以你的读和写只能按顺序。
你最后一行只有一个写,肯定死锁啊
应该不会吧
似乎你发现了GO的一个bug。 这样也会报错 complete <- "aaa1" msg := <-complete fmt.Println(msg) complete <- "aaa2" msg = <-complete fmt.Println(msg) 理论分析,不应该出现错误的。 同一个协程,先写再读,不应该报错的。 但是,实际运行,确实报这个错误。 难道channel必须在不同协程之间读写? 但是,channel加上缓冲区,var complete chan string = make(chan string,1),就不报错了。 似乎不带缓冲区的channel只支持不同协程之间读写? 期待高手来解答。
不是只有写没有读的问题。后面加上读,也会报错。 似乎是不带缓冲区的channel,用法特殊,只支持不同协程之间读写。或者根本就是GO的一个bug。 make(chan string,1),就不会报错。 很怪异。
正解
缓冲区满了自然死锁啊,没看出有什么地方有bug.
基础不扎实
概念: 无缓冲通道上的发送操作将会阻塞,直到另一个goroutine在对应通道上执行接收操作时,这时值传送完成,2个goroutine都可以执行
看懂了上面的话,就不难发现printstring(2)阻塞了main进程,去掉该行即可
把最后一行去掉就可以了,你的complete 是无缓冲通道 ,最后一行 printstring(2) 只有发送,没有接受,所以阻塞了
这个看你怎么理解了。 所以我说的是似乎,并没有说就是。 按照正常的理解,既然是一个通道,当然是发送接收而已,没那么多复杂的。同一个协程,我发了再收,应该也说得通,逻辑不矛盾。但是实际就是不行,和常规理解不同。 你当然可以说GO就是这样规定的,必须一个协程发,另一个协程收,没毛病。但这确实是不大合乎常规的理解。并且,显然,这里看出,带缓冲区和不带缓冲区,都是通道,存在一个很大区别。不注意,很可能犯这个错误。 所以,这个例子看出,尽量用带缓冲区的channel,哪怕是长度是1的缓冲区,也更不容易出错。按照常规理解,长度是1的缓冲区,就是一次只能发一个,实际就是和不带缓冲区的意思一样,当然这个是常规理解。但GO就是不按常规理解来,这个也不是GO的错。但这样,似乎欠缺点什么,至少不够友好。
楼主这个例子,提醒我们,以后尽量用长度为1的通道,来代替无缓冲区通道,实际两个通道的作用都相同,但前者的坑更少。
问题这里锁死的根本原因就是缓存区满了啊。
缓存区为0,无法写入,满了。
那如果缓存区为1,结果写入了2个数据,一样满了,也会死锁啊。
所以你就算缓存区部位0,也不能解决问题,反而使得问题隐藏的更深,带来跟多的问题。
这个不光实在体现在chan上,实际socket之类的链接都会有同样的问题
随便搜索下,比如
http://wiki.jikexueyuan.com/project/java-socket/socket-tcp-deadlock.html
http://www.voidcn.com/article/p-cbjwyyjf-btm.html
这个不算错误吧
你说的有道理 呵呵
make(chan string)和make(chan string,1)是两个不同的概念,前者是缓冲区长度为0,1个都存不了,所以写入时会阻塞。后者是缓冲区长度为1,能存一个,所以后者不阻塞。
“已经有一写一读,为什么还会死锁?怎么样才能不死锁呢?” 代码实现并没有一写一读。