一、基础简介
Golang使用Groutine和channels实现了CSP(Communicating Sequential Processes)模型,channles在goroutine的通信和同步中承担着重要的角色。
这种方式的优点是通过提供原子的通信原语,避免了竞态情形(race condition)下复杂的锁机制。
channel 可以看成一个 FIFO 队列,对 FIFO 队列的读写都是原子的操作,不需要加锁。
Golang 语言中Channel类型的定义格式如下:
chan Tr // 可以接收和发送类型为 Tr 的数据
chan <- float64 // 只可以用来发送 float64 类型的数据
<- chan int // 只可以用来接收 int 类型的数据
可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据。
默认情况下,channel发送方和接收方会一直阻塞直到对方准备好发送或者接收,这就使得Go语言无需加锁或者其他条件,天然支持了并发。
例:
c := make(chan bool) //创建一个无缓冲的bool型Channel
c <- x //向一个Channel发送一个值
<- c //从一个Channel中接收一个值
x = <- c //从Channel c接收一个值并将其存储到x中
x, ok = <- c //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false
使用 make 初始化Channel,还可以设置容量:
make(chan int, 100) //创建一个有缓冲的int型Channel
Channel缓冲介绍
容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓冲的大小。 如果没有设置容量,或者容量设置为0, 说明Channel没有缓冲。
在实际开发中,你可以在多个goroutine从/往一个channel 中 receive/send 数据, 不必考虑额外的同步措施。
Channel可以作为一个先入先出(FIFO)的队列,接收的数据和发送的数据的顺序是一致的,不带缓冲的Channel兼具通信和同步两种特性。
二、channel 应用实例
实例1代码如下
package main
import "fmt"
func main() {
ch1 := make(chan int,2)
//send
go func() {
for i := 0; i < 8; i++ {
fmt.Printf("Sender :sending element %v ..\n",i)
ch1 <- i
}
fmt.Println("Sender : close the channel ...")
close(ch1)
}()
//receiver
for {
elem, ok := <- ch1
if !ok {
fmt.Println("Receiver: closed channel..")
break
}
fmt.Printf("Receiver: received an element: %v \n",elem)
}
fmt.Println("main end..")
}
上面示例的程序中,我们创建了一个带缓冲的INT类型Channel,然后在一个goroutine 循环向 channel 中发送序列数据,然后在一个goroutine用 for循环方式读取 <-ch1 的内容。
在这里我们利用Channel 实现了线程间(准确的说,goroutine之间的)通信,因为channel发送方和接收方会一直阻塞直到对方准备好发送或者接收,这就使得我们在程序等待 ch1关闭而无需其他的同步操作。
- 缓存ch1 := make(chan int,0) 或 ch1 := make(chan int); 运行结果如下:
~/gomod/src/channel$ go run channel.go
Sender :sending element 0 ..
Sender :sending element 1 ..
Receiver: received an element: 0
Receiver: received an element: 1
Sender :sending element 2 ..
Sender :sending element 3 ..
Receiver: received an element: 2
Receiver: received an element: 3
Sender :sending element 4 ..
Sender :sending element 5 ..
Receiver: received an element: 4
Receiver: received an element: 5
Sender :sending element 6 ..
Sender :sending element 7 ..
Receiver: received an element: 6
Receiver: received an element: 7
Sender : close the channel ...
Receiver: closed channel..
main end..
- 缓存ch1 := make(chan int,5); 运行结果如下:
robot@ubuntu:~/gomod/src/channel$ go run channel.go
Sender :sending element 0 ..
Sender :sending element 1 ..
Sender :sending element 2 ..
Sender :sending element 3 ..
Sender :sending element 4 ..
Sender :sending element 5 ..
Sender :sending element 6 ..
Receiver: received an element: 0
Receiver: received an element: 1
Receiver: received an element: 2
Receiver: received an element: 3
Receiver: received an element: 4
Receiver: received an element: 5
Receiver: received an element: 6
Sender :sending element 7 ..
Sender : close the channel ...
Receiver: received an element: 7
Receiver: closed channel..
main end..
- 缓存ch1 := make(chan int,10); 运行结果如下:
robot@ubuntu:~/gomod/src/channel$ go run channel.go
Sender :sending element 0 ..
Sender :sending element 1 ..
Sender :sending element 2 ..
Sender :sending element 3 ..
Sender :sending element 4 ..
Sender :sending element 5 ..
Sender :sending element 6 ..
Sender :sending element 7 ..
Sender : close the channel ...
Receiver: received an element: 0
Receiver: received an element: 1
Receiver: received an element: 2
Receiver: received an element: 3
Receiver: received an element: 4
Receiver: received an element: 5
Receiver: received an element: 6
Receiver: received an element: 7
Receiver: closed channel..
main end..
实例2 代码如下:
package main
import (
"fmt"
"time"
)
func main() {
N := 10
exit := make (chan struct{})
done := make(chan struct{},N)
for i :=0; i< N; i++ {
go func(n int){
for {
select {
case <- exit:
fmt.Printf("worker goroutine # %d exit \n\r ",n)
done <- struct{}{}
return
case <- time.After(time.Second):
fmt.Printf("worker goroutine # %d is working ..\n", n)
}
}
}(i)
}
time.Sleep(3*time.Second)
fmt.Printf("sender exit channel... \n\r")
close(exit) //关闭 channel 通知协程退出线程
for i := 0; i < N;i++ {
<- done
}
fmt.Printf("main goroutine exit..\n")
}
运行效果如下:
robot@ubuntu:~/gomod/src/channel$ go run channel1.go
worker goroutine # 8 is working ..
worker goroutine # 5 is working ..
worker goroutine # 0 is working ..
worker goroutine # 2 is working ..
worker goroutine # 9 is working ..
worker goroutine # 4 is working ..
worker goroutine # 6 is working ..
worker goroutine # 7 is working ..
worker goroutine # 3 is working ..
worker goroutine # 1 is working ..
worker goroutine # 8 is working ..
worker goroutine # 9 is working ..
worker goroutine # 5 is working ..
worker goroutine # 0 is working ..
worker goroutine # 2 is working ..
worker goroutine # 6 is working ..
worker goroutine # 4 is working ..
worker goroutine # 7 is working ..
worker goroutine # 3 is working ..
worker goroutine # 1 is working ..
sender exit channel...
worker goroutine # 8 exit
worker goroutine # 2 exit
worker goroutine # 1 exit
worker goroutine # 0 exit
worker goroutine # 5 exit
worker goroutine # 9 exit
worker goroutine # 6 exit
worker goroutine # 7 exit
worker goroutine # 3 exit
worker goroutine # 4 exit
main goroutine exit..
关闭管道 exit 后,各 goroutine 携程检测到exit管道被关闭,自动退出协程。