GO通道chan

chan用于goroutine间的通信

1、基础

有缓冲通道和无缓冲通道

无缓冲通道:make(chan int)
有缓冲通道:make(chan int, 5)

无缓冲通道会导致发送和接收的goroutine同步化

主要体现在如下两点:
- 无缓冲通道会使发送goroutine阻塞,直到对应的chan上有接收者

package main

import (
    "fmt"
    "time"
)
var ch chan int
func main(){
    var i int = 9
    ch = make(chan int)
    go waiting()
    fmt.Println("向chan发送,时间:",time.Now())
    ch <- i
    fmt.Println("发送完毕,时间:", time.Now())
    //ch <- i
    time.Sleep(10000000000)

}

func waiting(){
    for {
        time.Sleep(10000000000)//接收协程等待10秒
        i, ok := <-ch
        if !ok {
            fmt.Println("chan is close!")
            break
        }
        fmt.Println("recv:", i)
    }
    fmt.Println("waiting end!")

}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
向chan发送,时间: 2017-12-07 16:12:04.856740287 +0800 CST
recv: 9
发送完毕,时间: 2017-12-07 16:12:14.857350085 +0800 CST
winterdeMacBook-Pro:chantest winter$
  • 同样,无缓冲通道会使接收goroutine阻塞,直到对应的chan上有发送
package main

import (
    "fmt"
    "time"
)
var ch chan int
func main(){
    var i int = 9
    ch = make(chan int)
    go waiting()
    fmt.Println("向chan发送,时间:",time.Now())
    time.Sleep(10000000000)//等待10秒后向chan发送数据
    ch <- i
    fmt.Println("发送完毕,时间:", time.Now())
    //ch <- i
    time.Sleep(10000000000)

}

func waiting(){
    for {
        //time.Sleep(10000000000)//接收协程等待10秒
        fmt.Println("开始接收chan中的数据,时间:", time.Now())
        i, ok := <-ch
        fmt.Println("chan中有数据,时间:",time.Now())
        if !ok {
            fmt.Println("chan is close!")
            break
        }
        fmt.Println("recv:", i)
    }
    fmt.Println("waiting end!")

}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
向chan发送,时间: 2017-12-07 16:17:35.598050582 +0800 CST
开始接收chan中的数据,时间: 2017-12-07 16:17:35.598060998 +0800 CST
发送完毕,时间: 2017-12-07 16:17:45.602548482 +0800 CST
chan中有数据,时间: 2017-12-07 16:17:45.602599793 +0800 CST
recv: 9
开始接收chan中的数据,时间: 2017-12-07 16:17:45.602665344 +0800 CST

缓冲通道,相当与一个队列,队列的最大长度在创建时指定

  • 一个空的chan,会导致接收goroutine阻塞
  • 一个满的chan,会导致发送goroutine阻塞

2、会导致程序挂掉的异常

nil通道的发送和接收

chan的零值是nil,没有初始化的等于nil

package main

import (
    "fmt"
)
func main(){
    var ch chan int
    if ch == nil {
        fmt.Println("ch is nil")
    }
}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
ch is nil

nil值的chan上的发送和接收会导致程序挂掉并提示“nil chan”

package main

import (
    "fmt"
)
var ch chan int
func main(){
    var i int = 9
    go waiting()
    ch <- i

}

func waiting(){
    i:= <-ch
    fmt.Println("recv:", i)
}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
        /Users/winter/code/go_project/src/test/chantest/chantest1.go:10 +0x64

goroutine 5 [chan receive (nil chan)]:
main.waiting()
        /Users/winter/code/go_project/src/test/chantest/chantest1.go:15 +0x50
created by main.main
        /Users/winter/code/go_project/src/test/chantest/chantest1.go:9 +0x35

向没有接收者的通道发送

向没有接受者的通道发送数据,程序会挂掉,会提示“deadlock”

package main

import (
    "fmt"
)
var ch chan int
func main(){
    var i int = 9
    ch = make(chan int)
    //ch没有接收者
    ch <- i

}

func waiting(){
    i:= <-ch
    fmt.Println("recv:", i)
}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /Users/winter/code/go_project/src/test/chantest/chantest1.go:11 +0x7f

chan后的关闭(close)操作

  • 在一个关闭后的chan上进行接收操作,将获取所有已经发送的值,直到chan为空,然后阻塞解除,会一直获取到chan元素类型对应的零值。即从一个关闭的chan上读取数据,永远不会阻塞
  • 在接收端可以判断chan是否关闭,但是没有办法在发送端判断chan是否关闭,所以只能在发送端关闭chan
  • 关闭chan是非必需的,因为GC回收chan,不是根据chan是否关闭,而是根据是否可以访问来决定的
  • 一般情况下,不要关闭chan,因为关闭一个chan存在以下风险:

关闭一个已经关闭的chan会导致程序崩溃
关闭一个零值chan会导致程序崩溃
chan关闭后,发送操作会导致程序崩溃

package main

import (
    "fmt"
    "time"
)
var ch chan int
func main(){
    var i int = 9
    ch = make(chan int)
    go waiting()
    ch <- i
    close(ch)
    ch <- i
    time.Sleep(10000000000)

}

func waiting(){
    for {
        i:= <-ch
        fmt.Println("recv:", i)
    }

}

输出:

winterdeMacBook-Pro:chantest winter$ ./chantest1
recv: 9
recv: 0
recv: 0
recv: 0
recv: 0
recv: 0
recv: 0
panic: send on closed channel

goroutine 1 [running]:
main.main()
        /Users/winter/code/go_project/src/test/chantest/chantest1.go:14 +0xda

可见,接收的goroutine收到最后一个元素后,一直获取到int的零值。直到主goroutine往关闭的chan发送导致程序挂掉。
那么如何判断一个chan是否已经被close?从chan读取数据,一般都简写成 i := <- ch,其实返回两个值,例如i, ok := <-ch,第一个返回值表示获取的元素,第二个返回值是bool型,等于false表示读取异常。所以可以根据这个返回值判断chan是否被close。

package main

import (
    "fmt"
    "time"
)
var ch chan int
func main(){
    var i int = 9
    ch = make(chan int)
    go waiting()
    ch <- i
    close(ch)
    //ch <- i
    time.Sleep(10000000000)

}

func waiting(){
    for {
        i, ok := <-ch
        if !ok {
            fmt.Println("chan is close!")
            break
        }
        fmt.Println("recv:", i)
    }
    fmt.Println("waiting end!")

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值