Go Channel的基本操作语法如下:
c := make(chan bool) //创建一个无缓冲的bool型Channel
c <- x //向一个Channel发送一个值
<- c //从一个Channel中接收一个值
x = <- c //从Channel c接收一个值并将其存储到x中
x, ok = <- c //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false
使用实例:
- 等待一个事件结束:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("开始主进程")
c := make(chan bool)
go func() {
fmt.Println("Doing something...")
time.Sleep(10 * time.Second)
//close(c) //close(c) 关闭通道后默认值是false
c <- true
}()
//<-c //从Channel中接收一个值,在channel没有值或者close之前一直处于阻塞状态
// 此处在sub goroutine的close(c)执行完成前,处于阻塞状态
a, ok := <-c
fmt.Println("a: ", a)
fmt.Println("ok: ", ok)
fmt.Println("主进程执行完")
}
- 协同多个Goroutines
func main() {
fmt.Println("start...")
ch := make(chan int)
var result int
go func() {
fmt.Println("goroutine1...")
var a int
a = 1
ch <- a
}()
go func() {
fmt.Println("goroutine2...")
var a int = 2
ch <- a
}()
go func() {
fmt.Println("goroutine3...")
ch <- 3
}()
for i := 0; i < 3; i++ {
result += <-ch
}
close(ch)
fmt.Println("result is:", result)
fmt.Println("end")
}
- select使用
select :选择处理列出的多个通信情况中的一个。
a. 使用select语句可以在多个可供选择的channel中读取任意一个数据执行。如果没有任何一个channel可以读取数据,则线程会被阻塞住,直到可以从某一个channel中读取数据为止。
b. 如果多个可以处理,随机选择一个。select语句不会循环如果需要循环读取,需要手动在select语句外加循环。
c. 如果没有通道操作可以处理并且写了 default 语句,它就会执行: default 永远是可运行的(这就是准备好了,可以执行)。
select监听数据进入通道,或者数据出通道
case后面的代码运行能是通道不阻塞,就会匹配成功。
select {
case x := <- somechan:
// … 使用x进行一些操作
case y, ok := <- someOtherchan:
// … 使用y进行一些操作,
// 检查ok值判断someOtherchan是否已经关闭
case outputChan <- z:
// … z值被成功发送到Channel上时
default:
// … 上面case均无法通信时,执行此分支
}
//主线程使用select语句从c、c2任意一个channel中读取数据。两个协程分布向c,c2中发送数据,其中一个在1s后发送,另一个在2s后发送。可以看到主线程一开始无法从任何一个channel中读取到数据,处于阻塞状态。在1s时收到了c2的数据,然后就会继续往下运行。
func main() {
c := make(chan int)
c2 := make(chan int)
go func() {
time.Sleep(10 * time.Second)
c <- 1
}()
go func() {
time.Sleep(5 * time.Second)
c2 <- 2
}()
select {
case i := <-c:
fmt.Printf("receive from c: %d\n", i)
case i := <- c2:
fmt.Printf("receive from c2: %d\n", i)
}
}
//主线程随机挑选一个仍有缓冲区channel发送数据,如果缓冲区已满,则这个channel的case语句将会被阻塞。
func main() {
c := make(chan int)
c2 := make(chan int)
go func() {
fmt.Printf("receive from c: %d\n", <-c)
}()
go func() {
fmt.Printf("receive from c2: %d\n", <-c2)
}()
time.Sleep(time.Second)
for {
select {
case c <- 1:
fmt.Printf("send c\n")
case c2 <- 2:
fmt.Printf("send c2\n")
default:
fmt.Printf(".....\n")
}
time.Sleep(time.Second)
}
}
总结
- channel有带缓冲区和不带缓冲区(相当于缓冲区容量为0)两种类型。当缓冲区已满时会阻塞发送者,当缓冲区已空时会阻塞接受者。channel是线程安全的。
- 使用close关键字可以关闭channel。向已关闭的channel的发数据会panic。已关闭的channel中仍然可以读取数据,可以通过接受ok参数判断是否是从一个已关闭的channel中读取的数据。
- 使用for range语法可以从channel中循环读取数据,若channel为空,则循环会被阻塞,关闭channel会跳出循环。