golang学习笔记:channel与《触不到的恋人》

本人初学golang,刚接触并发和channel这一部分时感觉很奇怪,写过一些代码,看过别人写的代码以后,大概对这个东西有了一些了解,在此记录,如果有大神看到有误之处请不吝赐教。

先不看channel的用法,先来说说这个channel是什么东西。我们把它看成一个信箱,电影《触不到的恋人》看过吗?比如,男主Alex和女主Kate只能通过这个信箱通信,因为他们生活在不同的时间线上,Alex在两年前,彼此之间是没有其他方式可以交换信息的。那Alex想啊,我问Kate明天的彩票号码,我不就可以发财了?现在假设这个信箱里只能放一封信,一封信里只能有一个彩票号码,然后他就啥也不干了,就等着Kate给他写信告诉他彩票号码,等啊等,Kate终于写了一封信放在信箱,Alex收到信就屁颠屁颠去买彩票了。
这里写图片描述
然后Alex又觉得,这样太慢了,必须收到一封信立马拿出来,Kate才能再往里放信,万一哪天他忘了拿信,那不是就少了一次中奖的机会?所以他改造了一下这个信箱,让它能容纳最多5封信,这下Kate可以随时给他写信了,不仅可以写彩票号码,还能互相传递一下爱意,美滋滋~
上面都是我瞎编的,我也没买过彩票(嘻嘻),主要就是帮助理解这个channel是干什么的。下面说一下具体到编程中应该怎么用。

channel是两个go routine之间通信使用的。go routine是啥就不用多说了吧,我们新建两个go routine,一个就叫Alex,一个叫Kate,然后有一个channel叫mailBox,那最初的故事就像这样:

package main

import (
    "fmt"
    "time"
)

func main() {
    mailBox := make(chan int)
    go Kate(12345, mailBox)
    go Alex(mailBox)
    time.Sleep(time.Second)
}

func Kate(lottery int, mailBox chan int) {
    mailBox <- lottery
}

func Alex(mailBox chan int)  {
    lottery := <- mailBox
    fmt.Println("I got the lottery number!", lottery)
}

其实这段代码包含3个go routine,一个是main函数的routine,一个是Kate,一个是Alex,我们作为看电影的观众其实是在两个主角的routine外的,对吧~所以我们就是main routine。
然后看一下两个主角,Kate的routine做的一件事情就是把彩票号码“12345”放到mailBox里面,然后Alex做的一件事就是把mailBox里的彩票号码取出来,然后输出。我们在main routine设置了一个sleep,否则两个routine还没执行完,main函数就结束了,两个routine就被回收了,我们是看不到输出的,也就是说我们把电影关掉了,那我们也不知道后面会发生什么。如果我们从Alex的角度看呢?也就是说把Alex放到main routine里,那代码就变成了这样:

package main

import (
    "fmt"
)

func main() {
    mailBox := make(chan int)
    go Kate(12345, mailBox)
    Alex(mailBox)
}

func Kate(lottery int, mailBox chan int) {
    mailBox <- lottery
}

func Alex(mailBox chan int)  {
    lottery := <- mailBox
    fmt.Println("I got the lottery number!", lottery)
}

执行结果还是一样的,为什么这次不用sleep了呢?因为从Alex的角度来看,他一定要等到Kate给他写信他才能买彩票,他是肯定不会放弃这次的发财机会的,所以整个routine就阻塞在那里,等待Kate往mailBox里放东西。

这段代码其实是不太科学的,因为Kate只写了一次信,其实她可以一直写信的,Alex也可以一直收信,所以我们用循环来表示Kate和Alex的动作:

package main

import (
    "fmt"
)

func main() {
    mailBox := make(chan int)
    go Kate(12345, mailBox)
    Alex(mailBox)
}

func Kate(lottery int, mailBox chan int) {
    for i := 0; i < 10; i++ {
        mailBox <- lottery + i
    }
    close(mailBox)
}

func Alex(mailBox chan int) {
    for lottery := range mailBox {
        fmt.Println("I got the lottery number!", lottery)
    }
}

Kate想要再次写信给Alex,就一定要等他把信箱里的信拿出来,才能放新的信进去,所以Alex没有拿信之前,Kate也是阻塞在那里的。可能有人注意到了Kate的循环结束后加了一个close方法,这是因为Kate需要告诉信箱她不爽了不干了,让Alex别等了。信箱关闭以后Alex还能再取到Kate最后写的一封信,如果不关闭mailBox,Alex就会一直等,但是一直等不到,就会发生死锁,系统报错fatal error: all goroutines are asleep - deadlock!

然后我们来看第二个故事,Alex把邮箱改造了一下,这时邮箱可以放5封信了,代码里可以在make的时候增加一个参数,像这样:mailBox := make(chan int, 5) 那Alex就不需要一直等了,他可以先去做其他事,想起来的时候就去取几封信(假设一次取3封吧),但是这个取信的顺序在go里面是有规定的,简单来说就是先进先出,代码变成了这样:

package main

import (
    "fmt"
    "time"
)

func main() {
    mailBox := make(chan int, 5) //channel缓冲长度为5
    go Kate(12345, mailBox)
    Alex(mailBox)
}

func Kate(lottery int, mailBox chan int) {
    for i := 0; i < 10; i++ {
        time.Sleep(time.Millisecond)
        mailBox <- lottery + i
        fmt.Printf("Kate putting lottery %d into the mailBox\n", i)
    }
    close(mailBox)
}

func Alex(mailBox chan int) {
    for {
        fmt.Println("Alex eating")
        fmt.Println("Alex sleeping")
        fmt.Println("Alex pooping")
        time.Sleep(time.Second) //Alex做一些别的事情
        ok := true
        lottery := -1
        for i := 0; i < 3; i++ { //循环三次取信
            lottery, ok = <-mailBox //如果mailBox关闭,ok为false,Alex不再取信
            if !ok {
                break
            }
            fmt.Println("Alex got the lottery number!", lottery)
        }
        if !ok { //如果mailBox关闭,电影结束
            break
        }
    }
}

这段代码的输出是这样的:

Alex eating
Alex sleeping
Alex pooping
Kate putting lottery 0 into the mailBox
Kate putting lottery 1 into the mailBox
Kate putting lottery 2 into the mailBox
Kate putting lottery 3 into the mailBox
Kate putting lottery 4 into the mailBox
Alex got the lottery number! 12345
Alex got the lottery number! 12346
Alex got the lottery number! 12347
Alex eating
Alex sleeping
Alex pooping
Kate putting lottery 5 into the mailBox
Kate putting lottery 6 into the mailBox
Kate putting lottery 7 into the mailBox
Alex got the lottery number! 12348
Alex got the lottery number! 12349
Alex got the lottery number! 12350
Alex eating
Alex sleeping
Alex pooping
Kate putting lottery 8 into the mailBox
Kate putting lottery 9 into the mailBox
Alex got the lottery number! 12351
Alex got the lottery number! 12352
Alex got the lottery number! 12353
Alex eating
Alex sleeping
Alex pooping
Alex got the lottery number! 12354

从输出我们可以看到,一开始Kate依次放进去5封信,然后Alex取出3封信,这时信箱里有3个空位,Kate放入3封信,Alex取走3封信,然后Kate 只有2封信要放了,信箱里现在有4封信,信箱关闭。Alex再取走3封,只剩1封信了,再取走1封信后,信箱关闭且没有信了,程序就结束了。从输出的彩票号码我们可以看出是符合先进先出的顺序的。

先写到这吧,channel的其他特性以后继续补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值