go语言基础学习之Channel

偶然的开始,莫明的结束,终究不懂,如何才能释怀曾经心动的驿站,曾经甜蜜的依偎,曾经情感的归依,怎能轻易割舍。为何你能如此说断就断,毫不留恋,难道曾经的心动只是彼此的错觉,耳畔的承诺只是随口敷衍。或许早已决定,这段恋爱只是今生短暂的插曲,无法断续为永恒

Posted by yishuifengxiao on 2022-08-21

Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。

它的操作符是箭头 <-

1
2
ch <- v    // 发送值v到Channel ch中
v := <-ch // 从Channel ch中接收数据,并将数据赋值给v

(箭头的指向就是数据的流向)

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

1
ch := make(chan int)

一 通道的声明与创建

1.1 定义格式

Channel类型的定义格式如下:

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

它包括三种类型的定义。可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。

1
2
3
chan T          // 可以接收和发送类型为 T 的数据
chan<- float64 // 只可以用来发送 float64 类型的数据
<-chan int // 只可以用来接收 int 类型的数据

伪代码如下:

1
2
3
4
//声明类型
var 通道名 chan 数据类型
//创建通道
通道名 = make(chan 数据类型)

<-总是优先和最左边的类型结合。

1
2
3
4
chan<- chan int    // 等价 chan<- (chan int)
chan<- <-chan int // 等价 chan<- (<-chan int)
<-chan <-chan int // 等价 <-chan (<-chan int)
chan (<-chan int)

1.2 创建与初始化

使用make初始化Channel,并且可以设置容量:

1
make(chan int, 100)

容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。

如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。

可以通过内建的close方法可以关闭Channel。

你可以在多个goroutine从/往 一个channel 中 receive/send 数据, 不必考虑额外的同步措施。

Channel可以作为一个先入先出(FIFO)的队列,接收的数据和发送的数据的顺序是一致的。

channel的 receive支持 multi-valued assignment,如

1
v, ok := <-ch

它可以用来检查Channel是否已经被关闭了。

二 接收 & 发送数据

对于同一个通道来讲,他的读数据 和 写数据 都是阻塞的。

1
2
3
4
//从通道读数据
data := <-a
//把数据写入通道
a <- data

例子如下:

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

import "fmt"

func main() {
// 首先创建一个bool类型的通道
var ch1 chan bool
ch1 = make(chan bool)
//下面启动一个go routine
go func() {
for i := 0; i < 10; i++ {
fmt.Println("子goroutine中, i: ", i)
}
fmt.Println("completed")
//循环结束后 向通道中写数据,表示要结束了
ch1 <- true
}()
//在主程序中读取数据
data := <-ch1
//打印一下 我们读到的数据
fmt.Println("main data: ", data)
fmt.Println("main goroutine completed")
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
子goroutine中, i:  0
子goroutine中, i: 1
子goroutine中, i: 2
子goroutine中, i: 3
子goroutine中, i: 4
子goroutine中, i: 5
子goroutine中, i: 6
子goroutine中, i: 7
子goroutine中, i: 8
子goroutine中, i: 9
completed
main data: true
main goroutine completed

我们的子goroutine里面 循环打印1~10, 打印完成之后 把chanel类型的ch1写为true,
这时候,主goroutine就可以根据这一条件进行下一步了,在此之前,其实就算主goroutine先抢到了资源,从ch1中读取数据,但是现在通道里面啥都没有,只能阻塞,然后乖乖交出资源给我们的子goroutine,直到循环结束写true入ch1。

需要注意的有以下几点:

  • chanel是需要指定类型的 ,nil类型的chanel不能直接使用。
  • chanel本身是同步的,同一时间只能有一条goroutine进行操作。
  • chanel是goroutine之间传递数据用的,chanel数据的发送和接收必须在不同的goroutine中,如果只有一条goroutine是用不上chanel的,这种情况会发生死锁(deadLock)。
  • 从chanel里面读数据立马就会被阻塞,直到有向chanel写数据的goroutine来。
  • 向chanel里面写数据立马就会被阻塞,直到有从chanel读数据的goroutine来。

(以上都是相对于没有缓存的通道而言,后面讲到的缓存通道在缓冲区满的时候才阻塞,而不是立刻阻塞)

2.1 send语句

send语句用来往Channel中发送数据, 如ch <- 3
它的定义如下:

1
2
SendStmt = Channel "<-" Expression .
Channel = Expression .

在通讯(communication)开始前channel和expression必选先求值出来(evaluated),比如下面的(3+4)先计算出7然后再发送给channel。

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
c := make(chan int)
defer close(c)
go func() {
c <- 3 + 4
}()
i := <-c
fmt.Println(i)
}

输出结果为:

1
7

send被执行前(proceed)通讯(communication)一直被阻塞着。如前所言,无缓存的channel只有在receiver准备好后send才被执行。如果有缓存,并且缓存未满,则send会被执行。

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

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

2.2 receive 操作符

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

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

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

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

1
2
3
x, ok := <-ch
x, ok = <-ch
var x, ok = <-ch

如果OK 是false,表明接收的x是产生的零值,这个channel被关闭了或者为空。

三 通道的类型

3.1 blocking

默认情况下,发送和接收会一直阻塞着,直到另一方准备好。这种方式可以用来在gororutine中进行同步,而不必使用显示的锁或者条件变量。

从无缓存的 channel 中读取消息会阻塞,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息。

如官方的例子中x, y := <-c, <-c这句会一直等待计算结果发送到channel中。

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

import "fmt"

func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
s1 := s[:len(s)/2]
s2 := s[len(s)/2:]
fmt.Println(s1)
fmt.Println(s2)
go sum(s1, c)
go sum(s2, c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}

输出结果为

1
2
3
[7 2 8]
[-9 4 0]
-5 17 12

3.2 Buffered Channels

make的第二个参数指定缓存的大小:ch := make(chan int, 100)

通过缓存的使用,可以尽量避免阻塞,提供应用的性能。

有缓存的 channel 的声明方式为指定 make 函数的第二个参数,该参数为 channel 缓存的容量

1
ch := make(chan int, 10)

有缓存的 channel 类似一个阻塞队列(采用环形数组实现)。当缓存未满时,向 channel 中发送消息时不会阻塞,当缓存满时,发送操作将被阻塞,直到有其他 goroutine 从中读取消息;相应的,当 channel 中消息不为空时,读取消息不会出现阻塞,当 channel 为空时,读取操作会造成阻塞,直到有 goroutine 向 channel 中写入消息。

1
2
3
4
ch := make(chan int, 3)

// blocked, read from empty buffered channel
<- ch
1
2
3
4
5
6
7
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3

// blocked, send to full buffered channel
ch <- 4

有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个数据值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也不同。

  • 只有通道中没有要接收的值时,接收动作才会阻塞。
  • 只有通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。

这导致有缓冲的通道和无缓冲的通道之间的一个很大的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的通道没有这种保证。

有缓冲的 channel 创建需要指定容量大小,写入数据超出大小时会写阻塞


四 通道的用法

4.1 Range

channel 也可以使用 range 取值,并且会一直从 channel 中读取数据,直到有 goroutine 对改 channel 执行 close 操作,循环才会结束。

for …… range语句可以处理Channel。

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

import (
"fmt"
"time"
)

func main() {
go func() {
time.Sleep(1 * time.Hour)
}()
c := make(chan int)
go func() {
for i := 0; i < 10; i = i + 1 {
c <- i
}
close(c)
}()
for i := range c {
fmt.Println(i)
}
fmt.Println("Finished")
}

range c产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭。上面的例子中如果把close(c)注释掉,程序会一直阻塞在for …… range那一行。

运行结果为

1
2
3
4
5
6
7
8
9
10
11
0
1
2
3
4
5
6
7
8
9
Finished

4.2 select

4.2.1 select基础说明

select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。

select随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。一个默认的子句应该总是可运行的。

这里的通信,可以简单的理解为IO(输入输出),例如如下代码

1
2
3
4
5
6
7
8
select {
case <-ch1:
// 如果从 ch1 信道成功接收数据,则执行该分支代码
case ch2 <- 1:
// 如果成功向 ch2 信道成功发送数据,则执行该分支代码
default:
// 如果上面都没有成功,则进入 default 分支处理流程
}

Go 编程语言中 select 语句的语法如下:

1
2
3
4
5
6
7
8
9
select {
case communication clause :
statement(s)
case communication clause :
statement(s)
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s)
}

以下描述了 select 语句的语法:

  • 每个case都必须是一个通信

  • 所有channel表达式都会被求值

  • 所有被发送的表达式都会被求值

  • 如果任意某个通信可以进行,它就执行;其他被忽略。

  • 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。
    否则:

    1. 如果有default子句,则执行该语句。
    2. 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。

注意点如下:

  1. select语句只能用于信道的读写操作
  2. select中的case条件(非阻塞)是并发执行的,select会选择先操作成功的那个case条件去执行,如果多个同时返回,则随机选择一个执行,此时将无法保证执行顺序。对于阻塞的case语句会直到其中有信道可以操作,如果有多个信道可操作,会随机选择其中一个 case 执行
  3. 对于case条件语句中,如果存在信道值为nil的读写操作,则该分支将被忽略,可以理解为从select语句中删除了这个case语句
  4. 如果有超时条件语句,判断逻辑为如果在这个时间段内一直没有满足条件的case,则执行这个超时case。如果此段时间内出现了可操作的case,则直接执行这个case。一般用超时语句代替了default语句
  5. 对于空的select{},会引起死锁
  6. 对于for中的select{}, 也有可能会引起cpu占用过高的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {
var c1, c2, c3 chan int
var i1, i2 int
select {
case i1 = <-c1:
fmt.Printf("received ", i1, " from c1\n")
case c2 <- i2:
fmt.Printf("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
if ok {
fmt.Printf("received ", i3, " from c3\n")
} else {
fmt.Printf("c3 is closed\n")
}
default:
fmt.Printf("no communication\n")
}
}

运行结果如下:

1
no communication

4.2.2 select在通道中的使用

select 用法类似与 IO 多路复用,可以同时监听多个 channel 的消息状态,看下面的例子

1
2
3
4
5
6
7
8
9
10
select {
case <- ch1:
...
case <- ch2:
...
case ch3 <- 10;
...
default:
...
}
  • select 可以同时监听多个 channel 的写入或读取
  • 执行 select 时,若只有一个 case 通过(不阻塞),则执行这个 case 块
  • 若有多个 case 通过,则随机挑选一个 case 执行
  • 若所有 case 均阻塞,且定义了 default 模块,则执行 default 模块。若未定义 default 模块,则 select 语句阻塞,直到有 case 被唤醒。
  • 使用 break 会跳出 select 块。

select语句选择一组可能的send操作和receive操作去处理。它类似switch,但是只是用来处理通讯(communication)操作。
它的case可以是send语句,也可以是receive语句,亦或者default

receive语句可以将值赋值给一个或者两个变量。它必须是一个receive操作。

最多允许有一个default case,它可以放在case列表的任何位置,尽管我们大部分会将它放在最后。

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
package main

import (
"fmt"
)

func execute(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
}()
execute(c, quit)
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
0
1
1
2
3
5
8
13
21
34
quit

如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。如果没有case需要处理,则会选择default去处理,如果default case存在的情况下。如果没有default case,则select语句会阻塞,直到某个case需要处理。

需要注意的是,nil channel上的操作会一直被阻塞,如果没有default case,只有nil channel的select会一直被阻塞。

select语句和switch语句一样,它不是循环,它只会选择一个case来处理,如果想一直处理channel,你可以在外面加一个无限的for循环:

1
2
3
4
5
6
7
8
9
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}

4.2.3 timeout

select有很重要的一个应用就是超时处理。 因为上面我们提到,如果没有case需要处理,select语句就会一直阻塞着。这时候我们可能就需要一个超时操作,用来处理超时的情况。
下面这个例子我们会在2秒后往channel c1中发送一个数据,但是select设置为1秒超时,因此我们会打印出timeout 1,而不是result 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"time"
)

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")
}
}

其实它利用的是time.After方法,它返回一个类型为<-chan Time的单向的channel,在指定的时间发送一个当前时间给返回的channel中。

4.3 设置超时时间

1
2
3
4
5
6
7
8
9
10
11
12
ch := make(chan struct{})

// finish task while send msg to ch
go doTask(ch)

timeout := time.After(5 * time.Second)
select {
case <- ch:
fmt.Println("task finished.")
case <- timeout:
fmt.Println("task timeout.")
}

4.4 quite channel

有一些场景中,一些 worker goroutine 需要一直循环处理信息,直到收到 quit 信号

1
2
3
4
5
6
7
8
9
10
msgCh := make(chan struct{})
quitCh := make(chan struct{})
for {
select {
case <- msgCh:
doWork()
case <- quitCh:
finish()
return
}

4.5 单向 channel

即只可写入或只可读的channel,事实上 channel 只读或只写都没有意义,所谓的单向 channel 其实知识声明时用,比如

1
func foo(ch chan<- int) <-chan int {...}

chan<- int 表示一个只可写入的 channel,<-chan int 表示一个只可读取的 channel。上面这个函数约定了 foo 内只能从向 ch 中写入数据,返回只一个只能读取的 channel,虽然使用普通的 channel 也没有问题,但这样在方法声明时约定可以防止 channel 被滥用,这种预防机制发生再编译期间。


四 Timer和Ticker

timer是一个定时器,代表未来的一个单一事件,你可以告诉timer你要等待多长时间,它提供一个Channel,在将来的那个时间那个Channel提供了一个时间值。下面的例子中第二行会阻塞2秒钟左右的时间,直到时间到了才会继续执行。

1
2
3
timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("Timer 1 expired")

当然如果你只是想单纯的等待的话,可以使用time.Sleep来实现。

你还可以使用timer.Stop来停止计时器。

1
2
3
4
5
6
7
8
9
timer2 := time.NewTimer(time.Second)
go func() {
<-timer2.C
fmt.Println("Timer 2 expired")
}()
stop2 := timer2.Stop()
if stop2 {
fmt.Println("Timer 2 stopped")
}

ticker是一个定时触发的计时器,它会以一个间隔(interval)往Channel发送一个事件(当前时间),而Channel的接收者可以以固定的时间间隔从Channel中读取事件。下面的例子中ticker每500毫秒触发一次,你可以观察输出的时间。

1
2
3
4
5
6
ticker := time.NewTicker(time.Millisecond * 500)
go func() {
for t := range ticker.C {
fmt.Println("Tick at", t)
}
}()

类似timer, ticker也可以通过Stop方法来停止。一旦它停止,接收者不再会从channel中接收数据了。

五 关闭与同步

5.1 close

内建的close方法可以用来关闭channel。

总结一下channel关闭后sender的receiver操作。
如果channel c已经被关闭,继续往它发送数据会导致panic: send on closed channel:

1
2
3
4
5
6
7
8
9
10
11
import "time"
func main() {
go func() {
time.Sleep(time.Hour)
}()
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
c <- 3
}

但是从这个关闭的channel中不但可以读取出已发送的数据,还可以不断的读取零值:

1
2
3
4
5
6
7
8
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
fmt.Println(<-c) //1
fmt.Println(<-c) //2
fmt.Println(<-c) //0
fmt.Println(<-c) //0

但是如果通过range读取,channel关闭后for循环会跳出:

1
2
3
4
5
6
7
c := make(chan int, 10)
c <- 1
c <- 2
close(c)
for i := range c {
fmt.Println(i)
}

通过i, ok := <-c可以查看Channel的状态,判断值是零值还是正常读取的值。

1
2
3
4
c := make(chan int, 10)
close(c)
i, ok := <-c
fmt.Printf("%d, %t", i, ok) //0, false

注意点如下:

  • 关闭一个未初始化(nil) 的 channel 会产生 panic
  • 重复关闭同一个 channel 会产生 panic
  • 向一个已关闭的 channel 中发送消息会产生 panic
  • 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为 false 的 ok-idiom,可以用它来判断 channel 是否关闭
  • 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息

channel 可以看成一个 FIFO 队列,对 FIFO 队列的读写都是原子的操作,不需要加锁。对 channel 的操作行为结果总结如下:

操作 nil channel closed channel not-closed non-nil channel
close panic panic 成功 close
ch <- 一直阻塞 panic 阻塞或成功写入数据
<- ch 一直阻塞 读取对应类型零值 阻塞或成功读取数据

读取一个已关闭的 channel 时,总是能读取到对应类型的零值,为了和读取非空未关闭 channel 的行为区别,可以使用两个接收值:

1
2
// ok is false when ch is closed
v, ok := <-ch

golang 中大部分类型都是值类型(只有 slice / channel / map 是引用类型),读/写类型是值类型的 channel 时,如果元素 size 比较大时,应该使用指针代替,避免频繁的内存拷贝开销。


5.1.1 一写多读

这种场景下这个唯一的写入端可以关闭 channel 用来通知读取端所有数据都已经写入完成了。读取端只需要用 for range 把 channel 中数据遍历完就可以了,当 channel 关闭时,for range 仍然会将 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
package main

import (
"fmt"
"sync"
)

func main() {
wg := &sync.WaitGroup{}
ch := make(chan int, 100)

send := func() {
for i := 0; i < 100; i++ {
ch <- i
}
// signal sending finish
close(ch)
}

recv := func(id int) {
defer wg.Done()
for i := range ch {
fmt.Printf("receiver #%d get %d\n", id, i)
}
fmt.Printf("receiver #%d exit\n", id)
}

wg.Add(3)
go recv(0)
go recv(1)
go recv(2)
send()

wg.Wait()
}

其中一次的运行结果如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
receiver #2 get 0
receiver #2 get 3
receiver #2 get 4
receiver #2 get 5
receiver #2 get 6
receiver #2 get 7
receiver #2 get 8
receiver #2 get 9
receiver #2 get 10
receiver #2 get 11
receiver #2 get 12
receiver #2 get 13
receiver #2 get 14
receiver #2 get 15
receiver #2 get 16
receiver #2 get 17
receiver #2 get 18
receiver #2 get 19
receiver #2 get 20
receiver #2 get 21
receiver #2 get 22
receiver #2 get 23
receiver #2 get 24
receiver #2 get 25
receiver #2 get 26
receiver #2 get 27
receiver #2 get 28
receiver #2 get 29
receiver #2 get 30
receiver #2 get 31
receiver #0 get 1
receiver #0 get 33
receiver #0 get 34
receiver #1 get 2
receiver #1 get 36
receiver #1 get 37
receiver #1 get 38
receiver #1 get 39
receiver #1 get 40
receiver #1 get 41
receiver #1 get 42
receiver #1 get 43
receiver #0 get 35
receiver #0 get 45
receiver #0 get 46
receiver #0 get 47
receiver #0 get 48
receiver #0 get 49
receiver #0 get 50
receiver #0 get 51
receiver #0 get 52
receiver #0 get 53
receiver #0 get 54
receiver #0 get 55
receiver #0 get 56
receiver #0 get 57
receiver #0 get 58
receiver #0 get 59
receiver #0 get 60
receiver #0 get 61
receiver #0 get 62
receiver #0 get 63
receiver #0 get 64
receiver #0 get 65
receiver #0 get 66
receiver #0 get 67
receiver #0 get 68
receiver #0 get 69
receiver #0 get 70
receiver #0 get 71
receiver #0 get 72
receiver #0 get 73
receiver #0 get 74
receiver #0 get 75
receiver #0 get 76
receiver #0 get 77
receiver #0 get 78
receiver #0 get 79
receiver #0 get 80
receiver #0 get 81
receiver #0 get 82
receiver #0 get 83
receiver #0 get 84
receiver #0 get 85
receiver #0 get 86
receiver #0 get 87
receiver #0 get 88
receiver #0 get 89
receiver #0 get 90
receiver #0 get 91
receiver #0 get 92
receiver #0 get 93
receiver #0 get 94
receiver #0 get 95
receiver #0 get 96
receiver #0 get 97
receiver #0 get 98
receiver #0 get 99
receiver #0 exit
receiver #2 get 32
receiver #2 exit
receiver #1 get 44
receiver #1 exit

5.1.2 多写一读

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
38
39
40
41
42
43
44
45
46
package main

import (
"fmt"
"sync"
)

func main() {
wg := &sync.WaitGroup{}
ch := make(chan int, 100)
done := make(chan struct{})

send := func(id int) {
defer wg.Done()
for i := 0; ; i++ {
select {
case <-done:
// get exit signal
fmt.Printf("sender #%d exit\n", id)
return
case ch <- id*1000 + i:
}
}
}

recv := func() {
count := 0
for i := range ch {
fmt.Printf("receiver get %d\n", i)
count++
if count >= 1000 {
// signal recving finish
close(done)
return
}
}
}

wg.Add(3)
go send(0)
go send(1)
go send(2)
recv()

wg.Wait()
}

5.1.3 多写多读

这种场景稍微复杂,和上面的例子一样,也需要设置一个额外 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
"fmt"
"sync"
"time"
)

func main() {
wg := &sync.WaitGroup{}
ch := make(chan int, 100)
done := make(chan struct{})

send := func(id int) {
defer wg.Done()
for i := 0; ; i++ {
select {
case <-done:
// get exit signal
fmt.Printf("sender #%d exit\n", id)
return
case ch <- id*1000 + i:
}
}
}

recv := func(id int) {
defer wg.Done()
for {
select {
case <-done:
// get exit signal
fmt.Printf("receiver #%d exit\n", id)
return
case i := <-ch:
fmt.Printf("receiver #%d get %d\n", id, i)
time.Sleep(time.Millisecond)
}
}
}

wg.Add(6)
go send(0)
go send(1)
go send(2)
go recv(0)
go recv(1)
go recv(2)

time.Sleep(time.Second)
// signal finish
close(done)
// wait all sender and receiver exit
wg.Wait()
}

其中一次运行结果如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
receiver get 2000
receiver get 2001
receiver get 2002
receiver get 2003
receiver get 2004
receiver get 2005
receiver get 2006
receiver get 2007
receiver get 2008
receiver get 2009
receiver get 2010
receiver get 2011
receiver get 2012
receiver get 2013
receiver get 2014
receiver get 2015
receiver get 2016
receiver get 2017
receiver get 2018
receiver get 2019
receiver get 2020
receiver get 2021
receiver get 2022
receiver get 2023
receiver get 2024
receiver get 2025
receiver get 2026
receiver get 2027
receiver get 2028
receiver get 2029
receiver get 2030
receiver get 2031
receiver get 2032
receiver get 2033
receiver get 2034
receiver get 2035
receiver get 2036
receiver get 2037
receiver get 2038
receiver get 2039
receiver get 2040
receiver get 2041
receiver get 2042
receiver get 2043
receiver get 2044
receiver get 2045
receiver get 2046
receiver get 2047
receiver get 2048
receiver get 2049
receiver get 2050
receiver get 2051
receiver get 2052
receiver get 2053
receiver get 2054
receiver get 2055
receiver get 2056
receiver get 2057
receiver get 2058
receiver get 2059
receiver get 2060
receiver get 2061
receiver get 2062
receiver get 2063
receiver get 2064
receiver get 2065
receiver get 2066
receiver get 2067
receiver get 2068
receiver get 2069
receiver get 2070
receiver get 2071
receiver get 2072
receiver get 2073
receiver get 2074
receiver get 2075
receiver get 2076
receiver get 2077
receiver get 2078
receiver get 2079
receiver get 2080
receiver get 2081
receiver get 2082
receiver get 2083
receiver get 2084
receiver get 2085
receiver get 2086
receiver get 2087
receiver get 2088
receiver get 2089
receiver get 2090
receiver get 2091
receiver get 2092
receiver get 2093
receiver get 2094
receiver get 2095
receiver get 2096
receiver get 2097
receiver get 2098
receiver get 2099
receiver get 2100
receiver get 2101
receiver get 0
receiver get 1000
receiver get 2102
receiver get 2103
receiver get 2104
receiver get 2105
receiver get 2106
receiver get 2107
receiver get 2108
receiver get 2109
receiver get 2110
receiver get 2111
receiver get 2112
receiver get 2113
receiver get 2114
receiver get 2115
receiver get 2116
receiver get 2117
receiver get 2118
receiver get 2119
receiver get 2120
receiver get 2121
receiver get 2122
receiver get 2123
receiver get 2124
receiver get 2125
receiver get 2126
receiver get 1
receiver get 1001
receiver get 2127
receiver get 2
receiver get 3
receiver get 4
receiver get 5
receiver get 6
receiver get 7
receiver get 8
receiver get 1002
receiver get 2128
receiver get 9
receiver get 1003
receiver get 2129
receiver get 10
receiver get 1004
receiver get 2130
receiver get 11
receiver get 1005
receiver get 2131
receiver get 12
receiver get 13
receiver get 14
receiver get 15
receiver get 16
receiver get 17
receiver get 1006
receiver get 2132
receiver get 18
receiver get 1007
receiver get 2133
receiver get 19
receiver get 20
receiver get 21
receiver get 22
receiver get 23
receiver get 24
receiver get 25
receiver get 26
receiver get 27
receiver get 28
receiver get 29
receiver get 30
receiver get 31
receiver get 32
receiver get 33
receiver get 34
receiver get 35
receiver get 36
receiver get 37
receiver get 38
receiver get 39
receiver get 40
receiver get 41
receiver get 42
receiver get 43
receiver get 44
receiver get 45
receiver get 46
receiver get 47
receiver get 48
receiver get 49
receiver get 50
receiver get 51
receiver get 52
receiver get 53
receiver get 54
receiver get 55
receiver get 56
receiver get 57
receiver get 1008
receiver get 2134
receiver get 58
receiver get 1009
receiver get 2135
receiver get 2136
receiver get 2137
receiver get 2138
receiver get 2139
receiver get 2140
receiver get 2141
receiver get 2142
receiver get 2143
receiver get 2144
receiver get 59
receiver get 1010
receiver get 2145
receiver get 60
receiver get 61
receiver get 62
receiver get 63
receiver get 64
receiver get 65
receiver get 66
receiver get 67
receiver get 68
receiver get 69
receiver get 70
receiver get 71
receiver get 72
receiver get 73
receiver get 74
receiver get 75
receiver get 76
receiver get 77
receiver get 78
receiver get 79
receiver get 80
receiver get 81
receiver get 82
receiver get 83
receiver get 84
receiver get 1011
receiver get 2146
receiver get 85
receiver get 86
receiver get 1012
receiver get 2147
receiver get 87
receiver get 1013
receiver get 1014
receiver get 1015
receiver get 1016
receiver get 1017
receiver get 1018
receiver get 1019
receiver get 1020
receiver get 1021
receiver get 1022
receiver get 1023
receiver get 1024
receiver get 1025
receiver get 1026
receiver get 1027
receiver get 1028
receiver get 1029
receiver get 1030
receiver get 1031
receiver get 2148
receiver get 2149
receiver get 88
receiver get 1032
receiver get 2150
receiver get 2151
receiver get 89
receiver get 1033
receiver get 2152
receiver get 90
receiver get 1034
receiver get 1035
receiver get 1036
receiver get 1037
receiver get 1038
receiver get 1039
receiver get 1040
receiver get 1041
receiver get 1042
receiver get 1043
receiver get 1044
receiver get 1045
receiver get 1046
receiver get 1047
receiver get 1048
receiver get 1049
receiver get 1050
receiver get 2153
receiver get 91
receiver get 1051
receiver get 2154
receiver get 92
receiver get 93
receiver get 94
receiver get 95
receiver get 96
receiver get 97
receiver get 98
receiver get 99
receiver get 100
receiver get 101
receiver get 102
receiver get 103
receiver get 104
receiver get 105
receiver get 106
receiver get 107
receiver get 1052
receiver get 2155
receiver get 108
receiver get 109
receiver get 110
receiver get 111
receiver get 112
receiver get 113
receiver get 114
receiver get 115
receiver get 116
receiver get 117
receiver get 118
receiver get 119
receiver get 120
receiver get 121
receiver get 122
receiver get 123
receiver get 124
receiver get 125
receiver get 126
receiver get 127
receiver get 128
receiver get 129
receiver get 130
receiver get 131
receiver get 132
receiver get 133
receiver get 134
receiver get 135
receiver get 136
receiver get 137
receiver get 138
receiver get 139
receiver get 140
receiver get 1053
receiver get 2156
receiver get 141
receiver get 1054
receiver get 1055
receiver get 1056
receiver get 1057
receiver get 2157
receiver get 142
receiver get 1058
receiver get 143
receiver get 2158
receiver get 1059
receiver get 144
receiver get 2159
receiver get 1060
receiver get 145
receiver get 2160
receiver get 1061
receiver get 146
receiver get 2161
receiver get 1062
receiver get 147
receiver get 2162
receiver get 1063
receiver get 148
receiver get 2163
receiver get 1064
receiver get 149
receiver get 2164
receiver get 1065
receiver get 150
receiver get 2165
receiver get 1066
receiver get 151
receiver get 152
receiver get 153
receiver get 154
receiver get 155
receiver get 156
receiver get 157
receiver get 158
receiver get 159
receiver get 160
receiver get 161
receiver get 162
receiver get 163
receiver get 164
receiver get 165
receiver get 166
receiver get 167
receiver get 168
receiver get 169
receiver get 170
receiver get 171
receiver get 172
receiver get 173
receiver get 174
receiver get 175
receiver get 176
receiver get 177
receiver get 178
receiver get 179
receiver get 180
receiver get 181
receiver get 182
receiver get 183
receiver get 184
receiver get 1067
receiver get 2166
receiver get 185
receiver get 1068
receiver get 2167
receiver get 186
receiver get 1069
receiver get 2168
receiver get 2169
receiver get 2170
receiver get 1070
receiver get 187
receiver get 2171
receiver get 1071
receiver get 188
receiver get 2172
receiver get 189
receiver get 2173
receiver get 190
receiver get 1072
receiver get 2174
receiver get 191
receiver get 1073
receiver get 2175
receiver get 2176
receiver get 192
receiver get 193
receiver get 1074
receiver get 2177
receiver get 194
receiver get 1075
receiver get 1076
receiver get 195
receiver get 2178
receiver get 1077
receiver get 196
receiver get 2179
receiver get 1078
receiver get 197
receiver get 2180
receiver get 1079
receiver get 198
receiver get 2181
receiver get 1080
receiver get 199
receiver get 2182
receiver get 1081
receiver get 200
receiver get 2183
receiver get 1082
receiver get 201
receiver get 2184
receiver get 1083
receiver get 202
receiver get 2185
receiver get 1084
receiver get 203
receiver get 2186
receiver get 1085
receiver get 1086
receiver get 2187
receiver get 204
receiver get 1087
receiver get 2188
receiver get 205
receiver get 1088
receiver get 2189
receiver get 2190
receiver get 2191
receiver get 206
receiver get 1089
receiver get 2192
receiver get 207
receiver get 1090
receiver get 2193
receiver get 208
receiver get 209
receiver get 1091
receiver get 2194
receiver get 210
receiver get 1092
receiver get 2195
receiver get 2196
receiver get 2197
receiver get 2198
receiver get 2199
receiver get 2200
receiver get 2201
receiver get 2202
receiver get 2203
receiver get 2204
receiver get 2205
receiver get 2206
receiver get 2207
receiver get 2208
receiver get 2209
receiver get 2210
receiver get 2211
receiver get 2212
receiver get 2213
receiver get 2214
receiver get 2215
receiver get 2216
receiver get 2217
receiver get 2218
receiver get 2219
receiver get 2220
receiver get 2221
receiver get 2222
receiver get 2223
receiver get 2224
receiver get 2225
receiver get 2226
receiver get 2227
receiver get 211
receiver get 1093
receiver get 2228
receiver get 2229
receiver get 2230
receiver get 212
receiver get 1094
receiver get 2231
receiver get 213
receiver get 1095
receiver get 1096
receiver get 1097
receiver get 1098
receiver get 1099
receiver get 1100
receiver get 1101
receiver get 1102
receiver get 1103
receiver get 1104
receiver get 1105
receiver get 1106
receiver get 1107
receiver get 1108
receiver get 1109
receiver get 1110
receiver get 1111
receiver get 1112
receiver get 1113
receiver get 1114
receiver get 1115
receiver get 1116
receiver get 1117
receiver get 1118
receiver get 1119
receiver get 1120
receiver get 2232
receiver get 214
receiver get 1121
receiver get 2233
receiver get 2234
receiver get 2235
receiver get 2236
receiver get 2237
receiver get 2238
receiver get 2239
receiver get 2240
receiver get 2241
receiver get 2242
receiver get 2243
receiver get 2244
receiver get 2245
receiver get 2246
receiver get 2247
receiver get 2248
receiver get 2249
receiver get 2250
receiver get 2251
receiver get 2252
receiver get 2253
receiver get 1122
receiver get 215
receiver get 2254
receiver get 1123
receiver get 216
receiver get 2255
receiver get 2256
receiver get 1124
receiver get 217
receiver get 2257
receiver get 1125
receiver get 218
receiver get 2258
receiver get 2259
receiver get 2260
receiver get 2261
receiver get 2262
receiver get 2263
receiver get 2264
receiver get 2265
receiver get 2266
receiver get 2267
receiver get 2268
receiver get 2269
receiver get 2270
receiver get 2271
receiver get 2272
receiver get 2273
receiver get 2274
receiver get 2275
receiver get 2276
receiver get 2277
receiver get 2278
receiver get 2279
receiver get 2280
receiver get 1126
receiver get 219
receiver get 2281
receiver get 2282
receiver get 1127
receiver get 2283
receiver get 1128
receiver get 2284
receiver get 1129
receiver get 2285
receiver get 220
receiver get 221
receiver get 222
receiver get 223
receiver get 224
receiver get 225
receiver get 226
receiver get 227
receiver get 228
receiver get 229
receiver get 230
receiver get 231
receiver get 232
receiver get 233
receiver get 234
receiver get 235
receiver get 236
receiver get 237
receiver get 238
receiver get 239
receiver get 240
receiver get 241
receiver get 242
receiver get 243
receiver get 244
receiver get 245
receiver get 246
receiver get 247
receiver get 248
receiver get 1130
receiver get 2286
receiver get 249
receiver get 1131
receiver get 2287
receiver get 2288
receiver get 2289
receiver get 2290
receiver get 2291
receiver get 2292
receiver get 2293
receiver get 2294
receiver get 2295
receiver get 2296
receiver get 2297
receiver get 2298
receiver get 2299
receiver get 2300
receiver get 2301
receiver get 2302
receiver get 2303
receiver get 2304
receiver get 2305
receiver get 2306
receiver get 2307
receiver get 2308
receiver get 2309
receiver get 2310
receiver get 2311
receiver get 2312
receiver get 2313
receiver get 2314
receiver get 2315
receiver get 2316
receiver get 2317
receiver get 2318
receiver get 2319
receiver get 2320
receiver get 1132
receiver get 250
receiver get 2321
receiver get 2322
receiver get 2323
receiver get 2324
receiver get 2325
receiver get 2326
receiver get 2327
receiver get 2328
receiver get 2329
receiver get 2330
receiver get 2331
receiver get 2332
receiver get 2333
receiver get 2334
receiver get 2335
receiver get 2336
receiver get 2337
receiver get 2338
receiver get 2339
receiver get 2340
receiver get 2341
receiver get 2342
receiver get 2343
receiver get 2344
receiver get 2345
receiver get 2346
receiver get 2347
receiver get 2348
receiver get 1133
receiver get 251
receiver get 2349
receiver get 1134
receiver get 252
receiver get 253
receiver get 254
receiver get 255
receiver get 256
receiver get 257
receiver get 258
receiver get 259
receiver get 260
receiver get 261
receiver get 262
receiver get 263
receiver get 264
receiver get 265
receiver get 266
receiver get 267
receiver get 268
receiver get 269
receiver get 2350
receiver get 2351
receiver get 2352
receiver get 2353
receiver get 2354
receiver get 2355
receiver get 2356
receiver get 270
receiver get 1135
receiver get 271
receiver get 2357
receiver get 1136
receiver get 272
receiver get 2358
receiver get 2359
receiver get 1137
receiver get 273
receiver get 2360
receiver get 1138
receiver get 274
receiver get 275
receiver get 276
receiver get 277
receiver get 278
receiver get 279
receiver get 280
receiver get 281
receiver get 282
receiver get 283
receiver get 284
receiver get 285
receiver get 286
receiver get 287
receiver get 288
receiver get 289
receiver get 290
receiver get 291
receiver get 292
receiver get 293
receiver get 294
receiver get 295
receiver get 296
receiver get 297
receiver get 298
receiver get 299
receiver get 300
receiver get 301
receiver get 302
receiver get 303
receiver get 304
receiver get 2361
receiver get 1139
receiver get 305
receiver get 2362
receiver get 1140
receiver get 306
receiver get 2363
receiver get 2364
receiver get 2365
receiver get 2366
receiver get 2367
receiver get 2368
receiver get 2369
receiver get 2370
receiver get 2371
receiver get 2372
receiver get 2373
receiver get 2374
receiver get 2375
receiver get 2376
receiver get 2377
receiver get 2378
receiver get 2379
receiver get 2380
receiver get 2381
receiver get 2382
receiver get 2383
receiver get 2384
receiver get 2385
receiver get 1141
receiver get 307
receiver get 2386
receiver get 1142
receiver get 2387
receiver get 308
receiver get 1143
receiver get 2388
receiver get 309
receiver get 1144
receiver get 2389
receiver get 310
receiver get 1145
receiver get 2390
receiver get 2391
receiver get 2392
receiver get 2393
receiver get 2394
receiver get 2395
receiver get 2396
receiver get 2397
receiver get 311
receiver get 1146
receiver get 2398
receiver get 312
receiver get 1147
receiver get 2399
receiver get 313
receiver get 1148
receiver get 2400
receiver get 314
receiver get 1149
receiver get 2401
receiver get 315
receiver get 1150
receiver get 2402
receiver get 316
receiver get 1151
receiver get 2403
receiver get 1152
receiver get 2404
receiver get 317
receiver get 1153
receiver get 1154
receiver get 1155
receiver get 1156
receiver get 1157
receiver get 1158
receiver get 1159
receiver get 1160
receiver get 2405
receiver get 318
receiver get 1161
receiver get 1162
receiver get 1163
receiver get 1164
receiver get 1165
receiver get 1166
receiver get 1167
receiver get 1168
receiver get 1169
receiver get 1170
receiver get 1171
receiver get 1172
receiver get 1173
receiver get 1174
receiver get 1175
receiver get 1176
receiver get 1177
receiver get 1178
receiver get 1179
receiver get 1180
receiver get 1181
receiver get 1182
receiver get 1183
receiver get 1184
receiver get 2406
receiver get 319
receiver get 1185
receiver get 2407
receiver get 320
receiver get 1186
receiver get 321
receiver get 2408
receiver get 2409
receiver get 1187
receiver get 322
receiver get 2410
receiver get 1188
receiver get 323
receiver get 2411
receiver get 1189
receiver get 324
receiver get 2412
receiver get 1190
receiver get 325
receiver get 2413
receiver get 1191
receiver get 326
receiver get 2414
receiver get 1192
receiver get 327
receiver get 2415
receiver get 1193
receiver get 328
receiver get 2416
receiver get 1194
receiver get 329
receiver get 2417
receiver get 2418
receiver get 2419
receiver get 1195
receiver get 330
receiver get 2420
receiver get 1196
receiver get 331
receiver get 2421
receiver get 1197
receiver get 332
receiver get 2422
receiver get 1198
receiver get 333
receiver get 2423
receiver get 1199
receiver get 334
receiver get 2424
receiver get 1200
receiver get 335
receiver get 2425
receiver get 1201
receiver get 336
receiver get 2426
receiver get 1202
receiver get 337
receiver get 2427
receiver get 1203
receiver get 338
receiver get 2428
receiver get 1204
receiver get 339
receiver get 2429
receiver get 1205
receiver get 340
receiver get 341
receiver get 342
receiver get 343
receiver get 344
receiver get 345
receiver get 346
receiver get 347
receiver get 348
receiver get 349
receiver get 350
receiver get 351
receiver get 352
receiver get 353
receiver get 354
receiver get 355
receiver get 356
receiver get 357
receiver get 358
receiver get 359
receiver get 360
receiver get 361
receiver get 362
receiver get 363
sender #0 exit
sender #1 exit
sender #2 exit

5.2 同步

channel可以用在goroutine之间的同步。
下面的例子中main goroutine通过done channel等待worker完成任务。 worker做完任务后只需往channel发送一个数据就可以通知main goroutine任务完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
}

六 使用场景

6.1 futures / promises

golang 虽然没有直接提供 futrue / promise 模型的操作原语,但通过 goroutine 和 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
package main

import (
"io/ioutil"
"log"
"net/http"
)

// RequestFuture, http request promise.
func RequestFuture(url string) <-chan []byte {
c := make(chan []byte, 1)
go func() {
var body []byte
defer func() {
c <- body
}()

res, err := http.Get(url)
if err != nil {
return
}
defer res.Body.Close()

body, _ = ioutil.ReadAll(res.Body)
}()

return c
}

func main() {
future := RequestFuture("https://api.github.com/users/octocat/orgs")
body := <-future
log.Printf("reponse length: %d", len(body))
}

6.2 条件变量(condition variable)

类型于 POSIX 接口中线程通知其他线程某个事件发生的条件变量,channel 的特性也可以用来当成协程之间同步的条件变量。因为 channel 只是用来通知,所以 channel 中具体的数据类型和值并不重要,这种场景一般用 strct {} 作为 channel 的类型。

6.2.1 一对一通知

类似 pthread_cond_signal() 的功能,用来在一个协程中通知另个某一个协程事件发生:

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

import (
"fmt"
"time"
)

func main() {
ch := make(chan struct{})
nums := make([]int, 100)

go func() {
time.Sleep(time.Second)
for i := 0; i < len(nums); i++ {
nums[i] = i
}
// send a finish signal
ch <- struct{}{}
}()

// wait for finish signal
<-ch
fmt.Println(nums)
}

6.2.2 广播通知

类似 pthread_cond_broadcast() 的功能。利用从已关闭的 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
38
package main

import (
"fmt"
"time"
)

func main() {
N := 10
exit := make(chan struct{})
done := make(chan struct{}, N)

// start N worker goroutines
for i := 0; i < N; i++ {
go func(n int) {
for {
select {
// wait for exit signal
case <-exit:
fmt.Printf("worker goroutine #%d exit\n", n)
done <- struct{}{}
return
case <-time.After(time.Second):
fmt.Printf("worker goroutine #%d is working...\n", n)
}
}
}(i)
}

time.Sleep(3 * time.Second)
// broadcast exit signal
close(exit)
// wait for all worker goroutines exit
for i := 0; i < N; i++ {
<-done
}
fmt.Println("main goroutine exit")
}

6.3 信号量

channel 的读/写相当于信号量的 P / V 操作,下面的示例程序中 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
package main

import (
"log"
"math/rand"
"time"
)

type Seat int
type Bar chan Seat

func (bar Bar) ServeConsumer(customerId int) {
log.Print("-> consumer#", customerId, " enters the bar")
seat := <-bar // need a seat to drink
log.Print("consumer#", customerId, " drinks at seat#", seat)
time.Sleep(time.Second * time.Duration(2+rand.Intn(6)))
log.Print("<- consumer#", customerId, " frees seat#", seat)
bar <- seat // free the seat and leave the bar
}

func main() {
rand.Seed(time.Now().UnixNano())

bar24x7 := make(Bar, 10) // the bar has 10 seats
// Place seats in an bar.
for seatId := 0; seatId < cap(bar24x7); seatId++ {
bar24x7 <- Seat(seatId) // none of the sends will block
}

// a new consumer try to enter the bar for each second
for customerId := 0; ; customerId++ {
time.Sleep(time.Second)
go bar24x7.ServeConsumer(customerId)
}
}

6.4 互斥量

互斥量相当于二元信号里,所以 cap 为 1 的 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
package main

import "fmt"

func main() {
mutex := make(chan struct{}, 1) // the capacity must be one

counter := 0
increase := func() {
mutex <- struct{}{} // lock
counter++
<-mutex // unlock
}

increase1000 := func(done chan<- struct{}) {
for i := 0; i < 1000; i++ {
increase()
}
done <- struct{}{}
}

done := make(chan struct{})
go increase1000(done)
go increase1000(done)
<-done; <-done
fmt.Println(counter) // 2000
}

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

https://www.topgoer.com/并发编程/channel.html