- channel+goroutine是go并发的大杀器
- channel可以用来停止信号(发送信号停止子goroutine的执行)
- 超时控制(websocket中长时间不活跃的用户可以直接断开链接)
- 定时任务的执行
- 解耦生产方和消费方
- 控制最大并发数量
案例见下面:
* channel和goroutine的结合是go并发编程的大杀器
* 而channel和select、cancel、timer的结合能实现各种各样的功能
* 1. 停止信号:关闭channel或向channel发送一个元素,使接收方通过channel获得信息后做相应的操作
* 2. 任务定时:
(1)超时控制
func main() {
c := make(chan int)
out := make(chan bool)
go func() {
go func() {
time.Sleep(time.Second)
c <- 2
}()
select {
case <-time.After(time.Second * 1):
fmt.Println("超时了")
case v := <-c:
fmt.Println("c中读取出的数据:", v)
}
out <- true
}()
<-out
}
(2)定期执行某个任务
func main() {
timer := time.NewTicker(time.Second)
for {
select {
case <-timer.C:
fmt.Println("执行了") // 每隔1秒执行一次
}
}
}
3. 解耦生产方和消费方
服务启动时,启动n个worker,作为工作协程池,这些协程工作在一个for无限循环里, 从某个channel消费工作任务并执行
案例:
func main() {
tasksChan := make(chan int, 100) // 任务队列
go workerTask(tasksChan) // 开启处理任务的协程池
// 发起任务
for i := 0; i < 10; i++ {
tasksChan <- i
}
select {
case <-time.After(time.Second * 3):
}
}
func workerTask(tasksChan chan int) {
// 开启5个协程去处理任务队列中的数据
GOS := 5
for i := 0; i < GOS; i++ {
// 局部变量在堆栈上存储,也是变量逃逸的一种场景(解决方法:使用闭包)
go func(i int) {
for {
value := <-tasksChan
fmt.Printf("finish task: %d by worker %d\n", value, i)
time.Sleep(time.Second)
}
}(i)
}
}
输出结果:
finish task: 1 by worker 4
finish task: 2 by worker 1
finish task: 3 by worker 2
finish task: 4 by worker 3
finish task: 0 by worker 0
finish task: 5 by worker 0
finish task: 6 by worker 1
finish task: 7 by worker 3
finish task: 8 by worker 2
finish task: 9 by worker 4
4. 控制并发数
有时需要定时执行几百个任务,例如每天定时按城市来执行一些离线计算的任务。但是并发数又不能太高,
因为任务执行过程依赖第三方的一些资源,对请求的速率有限制。这时就可以通过 channel 来控制并发数。
案例:
var limit = make(chan int, 3)
func main() {
// 通过channel控制最大并发数量
tasks := [...]int{11, 22, 33, 44, 55, 66, 77, 88, 99, 100}
for i, v := range tasks {
// 为每一个任务开启一个goroutine
go func(i, v int) {
// 通过channel控制goroutine最大并发数量
limit <- -1
fmt.Println(i, v)
time.Sleep(time.Second)
<-limit
}(i, v)
}
time.Sleep(time.Second * 4)
}