测试2
java-图灵-1
python-图灵-1

golang的Channel

Channel 是 golang 一个非常重要的概念,如果你是刚开始使用 golang 的开发者,你可能还没有真正接触这一概念,本篇我们将分析 golang 的Channel

1. 引入

要讲 Channel 就避不开 Goroutine -- 协程。闲话不说, 直接上个例子

Goroutine demo

package main

import (
    "fmt"
    "time"
)

func main() {
    origin := 1
    go doSomeCompute(origin)
    time.Sleep(5 * time.Second)
}

func doSomeCompute(num int) {
    result := num * 2
    fmt.Println(result)
    return
}

简单来说,例子中有一个 main 的协程,一个 doSomeCompute 的协程。还有个延时退出的方法等待计算协程计算结果。我们尝试思考这个例子的一些问题:

a. 如何获取 doSomeCompute 的计算结果?

b. 如何获取 doSomeCompute 的执行状态,当前是执行完了还是执行中?

如何解决这种问题呢?

Channel!

2. Channel

Channel怎么处理上面的问题?我们直接上代码:

举例

package main

import (
    "fmt"
)

func main() {
    origin := 1
    //一个无缓冲Channel
    res := make(chan int)
    go doSomeCompute(origin, res)
    fmt.Println(<-res)
}

func doSomeCompute(num int, res chan int) {
    result := num * 2
    res <- result
}

例子中,Channel 充当起了协程间通信的桥梁。Channel 可以传递到协程说明它是线程安全的,事实也是如此。Channel 可以理解为管道,协程 doSomeCompute 向 Channel 写入结果, main 中读取结果。注意,例子中读取 Channel 的地方会阻塞直到拿到计算结果,这样就解决了问题 a 和 b。

2. Channel 的方向性

上面的例子中,计算协程是负责计算并将计算结果写入 Channel ,如果我们希望保证计算协程不会从 Channel 中读取数据该怎么处理?很简单,看例子:

func doSomeCompute(num int, res chan<- int) {
    result := num * 2
    res <- result
}
chan<- intint<-chan int

3. 阻塞性质

fmt.Println(<-res)res

注意,无缓冲的 Channel 的读写都是阻塞的,有缓冲的 Channel 可以一直向里面写数据,直到缓存满才会阻塞。读取数据同理, 直至 Channel 为空才阻塞。

用一个典型的例子来说明缓冲和非缓冲 Channel 的区别:

package main

import "fmt"

func doSomeCompute(ch chan int) {
    fmt.Println("deadlock test")
    <- ch
}

func main() {
    ch := make(chan int)
    ch <- 1
    go doSomeCompute(ch)
}
chdoSomeCompute
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    C:/mygo/src/demo/blog.go:12 +0x73
exit status 2

如果改成有缓冲的 Channel :

package main

import (
    "fmt"
    "time"
)

func doSomeCompute(ch chan int) {
    fmt.Println("deadlock test")
    <- ch
}

func main() {
    ch := make(chan int, 1)
    ch <- 1
    go doSomeCompute(ch)
    time.Sleep(1 * time.Second)
}

有与有缓冲的 Channel 写入后不阻塞(下一次写入才会阻塞),程序会继续执行。

4. Channel 的数据结构

Channel 在 golang 中实际上就是个数据结构。在 Golang 源码中, Channel 的数据结构 Hchan 的定义如下:

struct  Hchan
{
    uint32  qcount;         // total data in the q
    uint32  dataqsiz;       // size of the circular q
    uint16  elemsize;
    bool    closed;
    uint8   elemalign;
    Alg*    elemalg;        // interface for element type
    uint32  sendx;          // send index
    uint32  recvx;          // receive index
    WaitQ   recvq;          // list of recv waiters
    WaitQ   sendq;          // list of send waiters
    Lock;
};

时间仓促,这次对 Channel 介绍写的有点简单粗暴,下次再写。

  • 还没有人评论,欢迎说说您的想法!