go深入解析channel

常见问题

  1. 往未初始化的Channel写数据,会发生什么?

    nil channel会永远阻塞对该channel的读写操作

    1
    2
    3
    4
      func main() {
    var cc chan int
    cc <- 1
    }

    运行后,报错:

    1
    2
    3
    fatal error: all goroutines are asleep - deadlock!
    goroutine 1 [chan send (nil chan)]:
    main.main()

    上面的意思是,在main goroutine线,期望从管道中获得一个数据,而这个数据必须是其他goroutine线放入管道的,但是其他goroutine线都已经执行完了(all goroutines are asleep),那么就永远不会有数据放入管道。所以,main goroutine线在等一个永远不会来的数据,那整个程序就永远等下去了。这显然是没有结果的,所以这个程序就说“算了吧,不坚持了,我自己自杀掉,报一个错给代码作者,我被deadlock了”

  2. channel实现等待一段时间超时后,再退出?

    time.After定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // After waits for the duration to elapse and then sends the current time
    // on the returned channel.
    // It is equivalent to NewTimer(d).C.
    // The underlying Timer is not recovered by the garbage collector
    // until the timer fires. If efficiency is a concern, use NewTimer
    // instead and call Timer.Stop if the timer is no longer needed.
    func After(d Duration) <-chan Time {
    return NewTimer(d).C
    }

    select + time.After实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    func main() {
    c1 := make(chan int)
    c2 := make(chan int)
    fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    go func() {
    time.Sleep(time.Duration(2 * time.Second))
    fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
    c1 <- 2
    }()
    select {
    case n := <-c1:
    fmt.Println("receive from c1: ", n)
    case n := <-c2:
    fmt.Println("receive from c2: ", n)
    case <-time.After(8 * time.Second):
    fmt.Println("timeout,no receive")
    }
    }

    select + for实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    func main() {
    stop := make(chan bool)
    go func() {
    for {
    select {
    case <-stop:
    // 收到stop channel,结束goroutine
    return
    default:
    // 不做处理,等待stop channel
    time.Sleep(2 * time.Second)
    }
    }
    }()
    time.Sleep(10 * time.Second)
    // 停止goroutine
    stop <- true
    time.Sleep(5 * time.Second)
    }

特性介绍

channel主要用于多个goroutine通讯,其内部实现了同步,确保并发安全,默认为同步模式。

同步方式需要发送和接受配对。否则会被阻塞,直到另一方准备好后被唤醒。

异步方式通过判断缓冲区来决定是否阻塞,如果缓冲区已满,则发送被阻塞;缓冲区为空,则接收被阻塞

实现原理

hchan

channel都是以hchan结构体形式存在,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type hchan struct {
qcount uint
dataqsiz uint
buf unsafe.Pointer
elemsize uint16
closed uint32
elemtype *_type
sendx uint
recvx uint
recvq waitq
sendq waitq

lock mutex
}

Reference


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!