Golang-Channel & Goroutine-进阶篇

基础篇简单了介绍Channel&Goroutine的基本使用方法
接下来就是实际应用的问题了

Select

实际例子上可能会有1-N个不等的chaneel
这时候就需要使用专门给channel使用的switch case,叫做Select
Select有几个地方要注意

只能给channel用假设同时有两个channel达成条件,他会随机选择一个case进行

语法请看综合範例2有演示

Goroutine

使用Goroutine最重要的
不要对routing的输出顺序以及时间做出假设

举个简单的例子,只要印出123

func main() {go func() {log.Println("123")}()//没有暂停1秒钟让另一个routing準备好,有可能程式会直接结束而不会输出任何东西time.Sleep(1 * time.Second)}

可以试试看把time.Sleep(1 * time.Second)给Remark,基本上是不会输出任何东西,至少我是没有成功过
因为Goroutine有极大的可能在main function执行完时而还没有开始执行
所以Goroutine的closure就会什么都没有执行就结束了

Channel搭配Goroutine

这是最重要也是最难的部分
在使用Channel的时候最常要注意的事情就是deaklock
关于deaklock的情形可以参考:https://github.com/aceld/golang/blob/main/5%E3%80%81channel.md

Unbuffered Channel

基础篇有提到UnBuffered Channel有着同步的特性
这种channel的两端会等待着写入与读取,而且因为没有Buffer,所以Channel的两端会同步资料

func main() {ch := make(chan bool)go func() {ch <- true}()// 虽然没有time.Sleep等待goroutine执行// 但最后有一个<-ch一直读取channel// 所以可以看到Print truelog.Println(<-ch)}

Buffered Channel

可以使用for range方法读取里面的资料

func main() {ch := make(chan int, 10)go func() {for i := 0; i < 10; i++ {ch <- i}close(ch)}()for value := range ch {log.Println(value)}}

综合範例1 - 1个Routing + 1个channel + for loop

从channel利用for loop读取资料,可以搭配基础篇服用
make channel的那一行可以加上cap玩看看,马上就能知道UnBuffered & Buffered的差异
close(ch)那段最重要,可以试试看remark的结果,保证deadlock

func main() {ch := make(chan int) // 可以加上cap,一样可以正常跑程式,而且可以看出UnBuffered & Buffered的差异go func() {for i := 0; i < 10; i++ {log.Println("Send value", i)ch <- i}close(ch) // close channel很重要,没有闭关的话,另一端一直在读取,而没有资料写入,就会产生deadlock}()for {val, ok := <-chif ok {log.Println("Receive value", val)} else {break}}}

综合範例2 - N个Routing + N个channel + for loop + select

执行这段程式码有个重点
不要预期routing的执行时间,先后顺序,一切的假设都不要有
for loop的部分是要一直读取channel value
而当x==20时就会触发事件s<-str
select接收到s channel的value时就会执行该case,跳出带有LOOP tag的for loop

func main() {str := "stop"c := make(chan int)s := make(chan string)for i := 0; i < 50; i++ {go func(x int) {if x == 20 {log.Println("goroutine will stop")s <- str} else {log.Println("Send value", x)c <- x}}(i)}LOOP:for {select {case v, ok := <-c:if ok {log.Println("Select value", v)}case str := <-s:log.Println(str)break LOOPdefault:log.Println("wait")}}}

小结

在使用Goroutine&channel的时候要注意的地方大致可以归类在以下几点

同步使用非同步使用channel是否已经闭关channel是否已满不要对routing做出执行顺序、执行时间的假设假设在不闭关channel的前提下,重複利用channel,同时又怕因为读取造成deadlock,可以用unbuffered channel,搭配sync.WaitGroup+for select来实现

然后要自己写一遍,多deadlock几次到差点想把笔电一分为二的时候你就会了(误

其他还有Package sync & Package runtime,就等下一篇吧


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章