Golang 语言 Channel 的使用方式
一、无缓存 channel
无缓冲channel 可用于两个goroutine 之间 传递信号,比如以下示例:
顺序打印1 至 100 的奇数和偶数:
import ("fmt""time"
)func main() {block := make(chan struct{})go odd(block)go even(block)time.Sleep(time.Second)fmt.Println("done")}func odd(block chan struct{}) {for i := 1; i < 100; i++ {<-blockif i%2 == 1 {fmt.Println("奇数:", i)}}
}func even(block chan struct{}) {for i := 1; i < 100; i++ {<-blockif i%2 == 0 {fmt.Println("偶数:", i)}}
}
上面这段代码,我们使用一个无缓冲channel 作为两个gorountie 之间的信号传递的桥梁。
主子 goroutine 之间传递信号
func main() {block := make(chan struct{})go func() {for i := 0; i < 10; i++ {fmt.Println(i)}close(block)}()<-blockfmt.Println("done")}
我们使用一个无缓冲channel 作为主子goroutine 之间的信号传递的桥梁,通过信号传递,主gorountine 运行结束直接再退出。
二、有缓冲 channel
有缓冲channel 可以作用于解耦操作,模拟消息队列。“生产者”和“消费者”只需各自处理channel ,实现解耦。
解耦生产者和消费者。
func main() {task := make(chan int, 10)go consumer(task)//生成者for i := 0; i < 10; i++ {task <- i}time.Sleep(time.Second * 2)
}func consumer(task <-chan int) {for i := 0; i < 10; i++ {go func(id int) {t := <-taskfmt.Println(id, t)}(i)}
}
我们使用一个有缓冲的channel , 将生产者,消费者做解耦操作
输出结果:
三、超时操作和定时器
我们还可以通过select 和channel ,实现超时操作和定时器
超时操作:
import ("fmt""time"
)func main() {c1 := make(chan string, 1)go func() {time.Sleep(2 * time.Second)c1 <- "result 1"}()select {case res := <-c1:fmt.Println(res)case <-time.After(1 * time.Second):fmt.Println("timeout 1")}c2 := make(chan string, 1)go func() {time.Sleep(2 * time.Second)c2 <- "result 2"}()select {case res := <-c2:fmt.Println(res)case <-time.After(3 * time.Second):fmt.Println("timeout 1")}}
通过c1 和 c2 两个channel ,分别模拟出超时和未超时场景
定时器:
func main() {ticker := time.NewTicker(500 * time.Millisecond)done := make(chan bool)go func() {for {select {case <-done:returncase t := <-ticker.C:fmt.Println("Tick at ", t)}}}()time.Sleep(1600 * time.Millisecond)ticker.Stop()done <- truefmt.Println("Ticker stopped")
}
我们定义一个打点器,每间隔500Ms执行一次操作,当打点stop时,通过一个无缓冲channel 传递退出信号
三、总结
本文我们介绍了一些关于 Channel 的使用方式,我们在阅读完本文后可以了解无缓冲 channel 作为信号传递的使用方式和有缓冲 channel 解耦操作的方式,以及 channel 与 select 配合使用的用法。