Go channel类型

taj3991 · · 1119 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Channel是Go中的一个核心类型,可以把它看成一个管道,Goroutine通过它可以发送或者接收数据并进行通讯。它的操作符号是 <- ,就像map和slice一样,channel必须先创建才能够使用:

ch=make(chan int) // int表示chan里面的数据类型
ch :=make(chan interface{}) //任意数据类型的chan

Channel类型

Channel类型的定义格式如下:

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

包括三种类型的定义。 <- 表示数据的流向,如果没有指定 <-,则表示chan既可以发送数据也可以接受数据。

Channel声明

chan int //既可以发送数据也可以接受数据
<- chan int  //只允许从chan接受int类型数据
chan <- int //只允许发送int类型数据到chan

Channel设置容量

make(chan int ,100)

//  Channel: The channel's buffer is initialized with the specified
//  buffer capacity. If zero, or the size is omitted, the channel is
//  unbuffered.
func make(t Type, size ...IntegerType) Type

参数100用来设置channel的缓存容量,也是channel最大可以容纳的元素数量。
元素数量超过缓存大小,向channel发送数据会阻塞。
元素数量为空,从channel接受数据会阻塞。
如果缓存大小设置为0,发送和接受都将被阻塞。

Channel类型安全
多个goroutine向channel发送或者接受数据,是线程安全的,不要采取额外的同步措施。

Channel数据顺序
chnnel是一个FIFO(先进先出)队列,向channel发送数据的顺序和从channel接受数据的顺序一致。

Channel closed
channel关闭之后,继续向channel发送数据会panic,从channel接受数据不会panic,会立即返回,返回的是chan数据类型的零值。

       ch :=make(chan int,10)
    close(ch)
    a :=<- ch
    println(a) //print 0
    //ch <- 1 //panic: send on closed channel

我们也可以通过以下代码来检查channel是否关闭

  ch :=make(chan int,10)
  close(ch)
  a,ok :=<-ch //ok==false,表示channel被关闭

利用这个特性我们可以用来为中间的任务执行步骤的设置超时时间,超过指定时间任务没有执行完成,关闭channel,主逻辑就继续向下执行。

Channel 遍历

通过range可以遍历输出channel中的元素,channle 被close之后依然会输出channel中的元素。以下代码是向缓冲区为10的元素发送数据,然后关闭channle之后依然可以遍历输出channel里面的元素:

func main() {
    c := make(chan int,10)
    for i := 0; i < 10; i = i + 1 {
        c <- i
    }
    close(c)

    for i := range c {
        fmt.Print(i)
    }
}
 //输出:0123456789

下面代码是向一个没有缓冲区的channel发送数据,通过range遍历从channel接受数据打印:

func main() {

    c := make(chan int)
    go func() {
        for i := 0; i < 10; i = i + 1 {
            c <- i
        }
        close(c) //必须close,否则遍历channel后会panic。会提示:fatal error: all goroutines are asleep - deadlock!
    }()

    for i := range c {
        fmt.Println(i)
    }
    fmt.Println("Finished")
}

Channel用于select语句
select语句选择一组send或者receive操作去处理。它的case可以是send语句,也可以是receive语句,亦或default。如果case有多个channel可以接受数据,那么Go会随机选择一个case去处理,如果没有任何一个case要处理,有default的情况下会执行default逻辑,如果没有default项,则会一直阻塞,直到满足某个case条件。

下面代码是打印斐波那契数列每一项,通过在select语句之外嵌套for循环可以一直处理,直到遇到退出语句。

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}
func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}


打印结果

0
1
1
2
3
5
8
13
21
34
quit

Channel用于timeout
select case使用channel会一直阻塞知道满足某个case条件才会退出。不过我们可以通过time.After(timeout) 设置超时时间,超时时间到后就会自动退出。

下面代码中channel c1休眠2秒后会写入数据,然后case满足,打印result 1。
不过打印的是timeout 1,因为第二个case语句设置了超时时间。

package main

import "time"
import "fmt"
func main() {
    c1 := make(chan string, 1)
    go func() {
        time.Sleep(time.Second * 2)
        c1 <- "result 1"
    }()
    
    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(time.Second * 1):
        fmt.Println("timeout 1")
    }
}

打印结果

timeout 1

Channle groutine同步

利用channel我们可以实现groutine之间相互通信。
下面的例子是启动一个groutine区完成任务,任务完成后想channel写入数据。
main方法收到写入数据信号就打印done,表示任务完成,否则一直阻塞知道groutine完成任务。

package main

import (
    "time"
)
func worker(done chan bool) {
    time.Sleep(time.Second)
    // 通知任务已完成
    done <- true
}
func main() {
    done := make(chan bool, 1)
    go worker(done)
    // 等待任务完成
    <-done
    println("done")
}

参考文章:https://colobu.com/2016/04/14/Golang-Channels/


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:taj3991

查看原文:Go channel类型

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1119 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传