channel的应用场景总结
1、信号传递
有 4 个 goroutine,编号为 1、2、3、4。每秒钟会有一个 goroutine 打印出它自己的编号,要求你编写程序,让输出的编号总是按照 1、2、3、4、1、2、3、4……这个顺序打印出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | type Token struct {} func newWorker(id int, ch chan Token, nextCh chan Token) { for { token := <-ch // 取得令牌 fmt.Println((id + 1)) // id从1开始 time.Sleep(time.Second) nextCh <- token } } func main() { chs := [] chan Token{make( chan Token), make( chan Token), make( chan Token), make( chan Token)} // 创建4个worker for i := 0; i < 4; i++ { go newWorker(i, chs[i], chs[(i+1)%4]) } //首先把令牌交给第一个worker chs[0] <- struct {}{} select {} } |
2、信号通知
使用 chan 实现程序的 graceful shutdown,在退出之前执行一些连接关闭、文件 close、缓存落盘等一些动作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | func main() { var closing = make( chan struct {}) var closed = make( chan struct {}) go func () { // 模拟业务处理 for { select { case <-closing: return default : // ....... 业务计算 time.Sleep(100 * time.Millisecond) } } }() // 处理CTRL+C等中断信号 termChan := make( chan os.Signal) signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM) <-termChan close(closing) // 执行退出之前的清理动作 go doCleanup(closed) select { case <-closed: case <-time.After(time.Second): fmt.Println( "清理超时,不等了" ) } fmt.Println( "优雅退出" ) } func doCleanup(closed chan struct {}) { time.Sleep((time.Minute)) close(closed) } |
3、使用channel的声明控制读写权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 只有generator进行对outCh进行写操作,返回声明 // <-chan int,可以防止其他协程乱用此通道,造成隐藏bug func generator(int n) <- chan int { outCh := make( chan int) go func (){ for i:=0;i<n;i++{ outCh<-i } }() return outCh } // consumer只读inCh的数据,声明为<-chan int // 可以防止它向inCh写数据 func consumer(inCh <- chan int) { for x := range inCh { fmt.Println(x) } } |
4、超时控制
使用select
和time.After
,看操作和定时器哪个先返回,处理先完成的,就达到了超时控制的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | func doWithTimeOut(timeout time.Duration) (int, error) { select { case ret := <-do(): return ret, nil case <-time.After(timeout): return 0, errors.New( "timeout" ) } } func do() <- chan int { outCh := make( chan int) go func () { // do work }() return outCh } |
5、定时任务
使用select
和time.
NewTicker,实现定时任务。
1 2 3 4 5 6 7 8 9 | func main() { timer := time.NewTicker(time.Second) for { select { case <-timer.C: fmt.Println( "执行了" ) // 每隔1秒执行一次 } } } |
6、控制并发数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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) } |
7、生产者消费者模型
服务启动时,启动n个worker,作为工作协程池,这些协程工作在一个for无限循环里, 从某个channel消费工作任务并执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) } } |
8、优雅退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package main import ( "fmt" "log" "os" "os/signal" "syscall" "time" ) func main() { var closing = make( chan struct {}) var closed = make( chan struct {}) go func () { for { select { case <-closing: return default : fmt.Println( "业务逻辑..." ) time.Sleep(1 * time.Second) } } }() termChan := make( chan os.Signal) // 监听退出信号 signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM) <-termChan // 退出中 close(closing) // 退出之前清理一下 go doCleanup(closed) select { case <-closed: case <-time.After(time.Second): log.Println( "清理超时不等了" ) } log.Println( "优雅退出" ) } func doCleanup(closed chan struct {}) { time.Sleep(time.Minute) // 清理完后退出 close(closed) } |
参考:(36条消息) Golang并发编程-Channel的使用场景分析_pbrong的博客-CSDN博客_golang使用场景
参考:(36条消息) Go channel的使用场景,用法总结_Mark66890620的博客-CSDN博客
参考:(36条消息) go channel原理及使用场景_六月的的博客-CSDN博客
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码