目录
- channel 介绍
- channel创建
- channel 操作符与Range
- channel读写与关闭
- channel类型-有缓存与无缓存
- channel阻塞
- 例1:channel定义
- 例2:缓冲区channel定义
- 例3:channel通知结束-通过通信来共享内存
- 例4:channel用在goroutine之间的同步
- 例5:channel等待结束结束-sync.WaitGroup
channel 介绍
channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。
channel 是 goroutine 之间通信的一种方式,可以类比成 Unix 中的进程的通信方式管道。
channel创建
和 map 和 slice 数据类型一样channel必须先创建再使用。
ch := make(chan int)
c和 map 类似,make 创建了一个底层数据结构的引用,当赋值或参数传递时,只是拷贝了一个 channel 引用,指向相同的 channel 对象。和其他引用类型一样,channel 的空值为 nil 。使用 == 可以对类型相同的 channel 进行比较,只有指向相同对象或同为 nil 时,才返回 true。
channel操作符与range(遍历)
操作符
它的操作符是箭头 <-
,其中(箭头的指向就是数据的流向。
ch <- v // 发送值v到Channel ch中
v := <-ch // 从Channel ch中接收数据,并将数据赋值给v
Range(遍历):for …… range语句可以处理Channel。
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")
}
channel读、写与关闭
向channel写(发送)数据
// 向channel中发送(写)数据。
ch <- x //send
从channel读(接收)数据
//receive:从 channel 中取(读) 出数据, ok用于判断channel是否关闭
x, ok := <- ch
//或
x = <- ch
channel关闭
close(chan变量)
与关闭相关的注意以下事项:
- 关闭一个未初始化(nil) 的 channel 会产生 panic
- 重复关闭同一个 channel 会产生 panic
- 向一个已关闭的 channel 中发送消息会产生 panic
- 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为 false 的 ok-idiom,可以用它来判断 channel 是否关闭
- 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息
channel类型
channel 分为不带缓存的 channel 和带缓存的 channel。
无缓存的 channel
从无缓存的 channel 中读取消息会阻塞,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息。
通过无缓存的 channel 进行通信时,接收者收到数据 happens before 发送者 goroutine 唤醒。
有缓存的 channel
有缓存的 channel 的声明方式为指定 make 函数的第二个参数,该参数为 channel 缓存的容量
ch := make(chan int, 10)
有缓存的 channel 类似一个阻塞队列(采用环形数组实现)。当缓存未满时,向 channel 中发送消息时不会阻塞,当缓存满时,发送操作将被阻塞,直到有其他 goroutine 从中读取消息;相应的,当 channel 中消息不为空时,读取消息不会出现阻塞,当 channel 为空时,读取操作会造成阻塞,直到有 goroutine 向 channel 中写入消息。
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
// blocked, send to full buffered channel
ch <- 4
通过 len 函数可以获得 chan 中的元素个数,通过 cap 函数可以得到 channel 的缓存长度。
blocking
默认情况下,发送和接收会一直阻塞着,直到另一方准备好。
这种方式可以用来在gororutine中进行数据同步,而不必使用显示的锁或者条件变量。
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)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
// receive from c
//x, y := <-c, <-c这句会一直等待计算结果发送到channel中
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
综合实例
例1:channel定义
/**
* chan <- string:<- 代表发数据
* <- chan int: 代表收数据
*/
func createWorker(id int) chan int {
c := make(chan int)
for {
fmt.Printf("worker %d receiver %d \n", id, n)
}
return c
}
func main() {
var channels [10]chan int
for i :=0; i<10 ; i++{
channels[i] = createWorker(i)
}
//worker只有有输入channel时,才打有数据打印输出
//1.分发数据
for i :=0; i<10 ; i++{
//string('a' + i) : a会因为i而变为b,c,d,f..
channels[i] <- 'a' + i
}
for i :=0; i<10 ; i++{
//<- 发送数据的意思
channels[i] <- 'A' + i
}
time.Sleep(time.Microsecond)
}
例2:缓冲区channel定义
package main
import (
"fmt"
"time"
)
func write(ch chan int){
for i := 0; i < 100; i++ {
ch <- i
}
}
func read(ch chan int){
for {
b,ok := <- ch
if ok == false {
fmt.Println("chan is close")
break
}
fmt.Println(b)
}
}
func main(){
intChan := make(chan int, 10)
go write(intChan)
go read(intChan)
time.Sleep(10*time.Second)
}
例3:channel通知结束-通过通信来共享内存
package main
import (
"fmt"
)
type worker struct {
in chan int
done chan bool
}
/**
*/
func doWorker(id int,c chan int,done chan bool) {
for n := range c { //待到c 被close时,退出
fmt.Printf("worker %d receiver %d \n", id, n)
//done <- true //必须有人收,否则会 fatal error: all goroutines are asleep - deadlock!
//修改如下
go func() {done <- true}()
}
}
/**
* chan <- string:<- 代表发数据
* <- chan int: 代表收数据
*/
func createWorker(id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
//处理
go doWorker(id,w.in,w.done)
return w
}
//func closeChannel(w worker) {
// close(w.in)
// w.done <- false
//}
func channelDemo() {
var workers [10]worker
for i :=0; i<10 ; i++{
workers[i] = createWorker(i)
}
//worker只有有输入channel时,才打有数据打印输出
//1.发数据
for i ,_:= range workers {
workers[i].in <- 'a' + i
}
//发数据
for i ,_:= range workers {
//<- 发送数据的意思
workers[i].in <- 'A' + i
}
for _,worker := range workers{
<- worker.done
<- worker.done
}
}
func main() {
channelDemo()
//time.Sleep(time.Microsecond)
//bufferChannel()
//channelClose()
}
例4:channel用在goroutine之间的同步
channel可以用在goroutine之间的同步。worker做完任务后只需往channel发送一个数据就可以通知main goroutine任务完成。
import (
"fmt"
"time"
)
func worker(done chan bool) {
time.Sleep(time.Second)
// 通知任务已完成
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
// 等待任务完成
<-done
}
例4:channel等待结束结束-sync.WaitGroup
package main
import (
"fmt"
"sync"
)
type worker struct {
in chan int
wg *sync.WaitGroup
}
/**
sync.WaitGroup方法:
var wg sync.WaitGroup
wg.add
wg.done
wg.wait
*/
func doWorker(id int,c chan int,wg *sync.WaitGroup) {
for n := range c { //待到c 被close时,退出
fmt.Printf("worker %d receiver %d \n", id, n)
wg.Done()
}
}
func createWorker(id int,wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
wg: wg,
}
//处理
go doWorker(id,w.in,wg)
return w
}
func channelDemo() {
var wg sync.WaitGroup
var workers [10]worker
for i :=0; i<10 ; i++{
//只是传递地址给到createWoker
workers[i] = createWorker(i,&wg)
}
//增加20个任务等待结束
wg.Add(20)
//1.发数据
for i ,_:= range workers {
workers[i].in <- 'a' + i
//或者 循环内: wg.Add(1)
}
//发数据: <-
for i ,_:= range workers {
workers[i].in <- 'A' + i
}
wg.Wait()
}
func main() {
channelDemo()
}
例5: 收发数据
package main
import (
"fmt"
"time"
)
/**
* chan <- string:<- 代表发数据
* <- chan int: 代表收数据
*/
func createWorker(id int,c chan string) chan string {
for {
fmt.Printf("worker %d receiver %s \n",id, <- c)
}
return c
}
func channelDemo() {
var channels [10]chan string
for i :=0; i<10 ; i++{
channels[i] = make(chan string)
go createWorker(i,channels[i])
}
//worker只有有输入channel时,才打有数据打印输出
//1.分发数据
for i :=0; i<10 ; i++{
//string('a' + i) : a会因为i而变为b,c,d,f..
channels[i] <- string('a' + i)
}
for i :=0; i<10 ; i++{
//<- 发送数据的意思
channels[i] <- string('A' + i)
}
time.Sleep(time.Microsecond)
}
func main() {
channelDemo()
time.Sleep(time.Microsecond)
}