go channel

channel 是 Go 中的一个核心类型, 可以把它看成一个管道, 通过它并发核心单元就可以发送或者接收数据进行通讯。goroutine 是 Go 语言的基本调度单位, 而 channels 则是它们之间的通信机制。操作符 <- 用来指定管道的方向,发送或接收。如果未指定方向,则为双向管道。golang 的 channel 就是一个 环形队列/ringbuffer 的实现。 我们称 chan 为管理结构,channel 里面可以放任何类型的对象,我们称之为元素。

Channel 定义

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

可选的 <- 代表 channel 的方向 (是数据的流向)。如果没有指定方向,那么 Channel 就是双向的,既可以接收数据,也可以发送数据。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<-          // channel 的操作符
ch <- v     // 发送值 v 到 Channel ch 中
v := <-ch   // 从 Channel ch 中接收数据, 并将数据赋值给 v



var foo chan T          // 可以接收和发送类型为 T 的数据
var foo chan<- float64  // 只可以用来发送 float64 类型的数据
var foo <-chan int      // 只可以用来接收 int 类型的数据
// <- 总是优先和最左边的类型结合。(The <- operator associates with the leftmost chan possible)

chan<- chan int    // 等价 chan<- (chan int)
chan<- <-chan int  // 等价 chan<- (<-chan int)
<-chan <-chan int  // 等价 <-chan (<-chan int)
chan (<-chan int)

// channel 定义
var dataChan <-chan []byte

// 无缓冲的 channel
// 使用 make 初始化 Channel, 并且可以设置容量, channel 初始化, 初始化之后才能使用
// 未设置容量的 channel, 如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯
// 无缓冲的 channel 由于没有缓冲发送和接收需要同步.
// channel 无缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据。
dataChan := make(<-chan []byte)
// 容量为100的 channel
ch := make(chan int, 100)
// 容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。

// 创建一个双向channel, interface{}表示chan可以为任何类型
foo := make(chan interface{})

// 可以通过内建的close方法可以关闭Channel。
close(foo)

// channel的 receive支持 multi-valued assignment,如
v, ok := <-ch

往一个已经被 close 的 channe l中继续发送数据会导致 run-time panic。

往nil channel 中发送数据会一致被阻塞着。

从一个 nil channel 中接收数据会一直被 block。

从一个被 close 的 channel 中接收数据不会被阻塞,而是立即返回,返回值是channel里的元素类型的零值 (zero value, int:0, string:"", float:0)。

channel 有发送和接受两个主要操作。发送和接收两个操作都使用 <-运算符。在发送语句中,channel 放<-运算符左边。在接收语句中,channel放<-运算符右边。一个不使用接收结果的接收操作也是合法的。

1
2
3
4
5
6
// 发送操作
ch <- x 
// 接收操作
x = <-ch 
// 忽略接收到的值,合法
<-ch     

就像 map 和 slice 数据类型一样, channel 必须先创建再使用

1
2
3
4
5
6
7
// 创建 channel
make (chan type)
make (chan type,N) // 表示该channel自带N个type类型大小的buffer,只有该chan满/空时,调用方才会被阻塞
ch := make(chan int)

// 使用make初始化Channel,并且可以设置容量:
make(chan int, 100)

for 可以处理 channel

1
2
3
for i := range c {
        fmt.Println(i)
    }

receive 操作符

<-ch 用来从 channel ch 中接收数据,这个表达式会一直被 block, 直到有数据可以接收。

从一个 nil channel 中接收数据会一直被block。

从一个被 close 的 channel 中接收数据不会被阻塞,而是立即返回,接收完已发送的数据后会返回元素类型的零值(zero value)。

如前所述,你可以使用一个额外的返回参数来检查channel是否关闭。

http://colobu.com/2016/04/14/Golang-Channels/

使用 chan struct{} 作为信号 channel

场景:使用channel 传递信号,而不是传递数据时 原理:没数据需要传递时,传递空struct 用法:

1
2
3
4
5
6
// 上例中的 Handler.stopCh 就是一个例子,
// stopCh 并不需要传递任何数据, 只是要给所有协程发送退出的信号
type Handler struct {
    stopCh chan struct{}
    reqCh chan *Request
}

通常 struct{} 类型 channel的用法是使用同步,一般不需要往channel里面写数据,只有读等待,而读等待会在channel被关闭的时候返回。

往chann struct{} 写入数据 另一个问题,我们能不能往 struct{} 类型的channel里面写数据呢,答案当然也是可以的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
    "time"
    "log"
)

var ch chan struct{} = make(chan struct{})

func foo() {
    ch <- struct{}{}
    log.Println("foo() 111");
    time.Sleep(5 * time.Second)
    log.Println("foo() 222");
    close(ch)
    log.Println("foo() 333");
}

func main() {

    log.Println("main() 111");
    go foo()
    log.Println("main() 222");
    <-ch
    log.Println("main() 333");
}

在foo()入口处给ch赋了一个值 注意写法是"struct{}{}",第一个”{}“对表示类型,第二个”{}“对表示一个类型对象实例。

作者:CodingCode 链接:https://www.jianshu.com/p/7f45d7989f3a 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

https://segmentfault.com/a/1190000017958702

如前所述,你可以使用一个额外的返回参数来检查channel是否关闭。

https://www.jianshu.com/p/d24dfbb33781

https://go101.org/article/channel-closing.html

关闭channel Channel支持close操作,用于关闭channel,后面对该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel进行接收操作依然可以接受到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据。 从已经关闭的channel中读: intStream := make(chan int) close(intStream) integer, ok := <- intStream fmt.Pritf("(%v): %v”, ok, integer) // (false): 0 复制代码上面例子中通过返回值ok来判断channel是否关闭,我们还可以通过range这种更优雅的方式来处理已经关闭的channel: intStream := make(chan int) go func() { defer close(intStream) for i:=1; i<=5; i++{ intStream <- i } }()

for integer := range intStream { fmt.Printf("%v “, integer) } // 1 2 3 4 5

作者:彬叔 链接:https://juejin.cn/post/6844903623667744781 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

https://colobu.com/2016/04/14/Golang-Channels/

https://zhuanlan.zhihu.com/p/299592156