我们知道 go 中有个很重要的数据结构叫做 channel-通道,通过其特性,我们可以完成很多功能,自然就对应到很多应用场景了。
1.应用场景
channel 的一些应用场景包括不限于下面的这些:
- 1.
同步
,用来在多个协程之间的同步操作,如同一时间,只能有一个协程工作,其他协程只能等待,这里也可以对应到同步互斥的场景
- 2.
数据传递
,可通过 channel 在多个协程间进行数据传递,比如在 生产者-消费者 模式中使用,生产者向 channel 发送数据,消费者从 channel 中消费数据,也就涉及到对 channel 写-读 操作
- 3.
管道
,channel 作为管道,可将数据从一处转移到其他处,在数据流中常见
- 4.
缓冲
,在有缓存的 channel 中发送数据,没有读取的情况下,就可以将数据缓存起来,有助于缓解读者压力
- 5.
信号通知
,通过 channel 的读写完成信号通知
- 6.
范围迭代
,通过 for val := range chan
的操作,当遇到 channel 关闭的时候就会退出迭代,从而完成 channel 数据所有接收
- 7.
并发控制
,通过 channel 的有缓存类型,限制一定的并发数量,不至于无限制并发消耗系统资源
- 8.
定时器
,有很多标准库的结构体都带有 channel 功能,比如 time.Timer,就是一个定时器,通过定时器定期执行某些任务
以上是涉及到 channel 一些应用场景,以下是应用场景的一些实现,见下面的代码示例。
2.应用场景示例
2.1 并发控制
| package main |
| |
| import ( |
| "fmt" |
| "time" |
| ) |
| |
| // 通过限制 limit 的缓存数量,决定并发时有多少协程在并行运行 |
| var limit = make(chan struct{}, 3) |
| |
| /** |
| Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 0 exec i : 0, v: 1 |
| Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 11 exec i : 11, v: 12 |
| Time: 2024-07-13T11:44:01.9047944+08:00, Goroutine: 4 exec i : 4, v: 5 |
| Time: 2024-07-13T11:44:02.9200384+08:00, Goroutine: 8 exec i : 8, v: 9 |
| Time: 2024-07-13T11:44:02.9200384+08:00, Goroutine: 9 exec i : 9, v: 10 |
| Time: 2024-07-13T11:44:02.9201398+08:00, Goroutine: 10 exec i : 10, v: 11 |
| Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 5 exec i : 5, v: 6 |
| Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 1 exec i : 1, v: 2 |
| Time: 2024-07-13T11:44:03.9207458+08:00, Goroutine: 2 exec i : 2, v: 3 |
| Time: 2024-07-13T11:44:04.9240989+08:00, Goroutine: 6 exec i : 6, v: 7 |
| Time: 2024-07-13T11:44:04.9243366+08:00, Goroutine: 3 exec i : 3, v: 4 |
| Time: 2024-07-13T11:44:04.9243366+08:00, Goroutine: 7 exec i : 7, v: 8 |
| Time: 2024-07-13T11:44:07+08:00, 主线程退出! |
| */ |
| func main() { |
| tasks := []int{1, 2, 3, 4, 5, 6 ,7, 8, 9, 10, 11, 12} |
| for i, v := range tasks { |
| // 每个task开启一个协程 |
| go func(i, v int) { |
| // 通过chan控制并发 |
| limit <- struct{}{} |
| |
| // 具体的任务执行 |
| fmt.Printf("Time: %v, Goroutine: %v exec i : %d, v: %v\n", time.Now().Format(time.RFC3339Nano), i, i, v) |
| time.Sleep(time.Second) |
| |
| <- limit |
| }(i, v) |
| } |
| |
| time.Sleep(6 * time.Second) |
| fmt.Printf("Time: %v, 主线程退出!", time.Now().Format(time.RFC3339)) |
| } |
2.2 管道 | 范围迭代 | 数据传输
| package main |
| |
| import ( |
| "fmt" |
| "math/rand" |
| "time" |
| ) |
| |
| func main() { |
| numCh := make(chan int) |
| |
| for i := 0; i < 5; i++ { |
| go sender(numCh) |
| } |
| |
| for val := range numCh { |
| fmt.Println("Main recv val: ", val) |
| time.Sleep(time.Second*2) |
| } |
| |
| fmt.Println("Data transform model Done.") |
| } |
| |
| func sender(num chan int) { |
| randomNum := rand.Intn(100) |
| num <- randomNum |
| fmt.Println("Send val to chan:", randomNum) |
| time.Sleep(time.Second) |
| } |
2.3 数据传递 -> 生产者-消费者模型
| package main |
| |
| import ( |
| "fmt" |
| "math/rand" |
| "sync" |
| "time" |
| ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func main() { |
| var wg sync.WaitGroup |
| wg.Add(2) |
| |
| numCh := make(chan int, 1) |
| go producer(numCh, &wg) |
| go consumer(numCh, &wg) |
| |
| wg.Wait() |
| fmt.Println("本次生产者-消费者模型结束.") |
| } |
| |
| func producer(num chan int, wg *sync.WaitGroup) { |
| defer wg.Done() |
| count := 0 |
| for count < 1 { |
| for i := 0; i < 3; i++ { |
| randomNum := rand.Intn(100) |
| num <- randomNum |
| fmt.Printf("生产者持续发送消息:%d\n", randomNum) |
| } |
| time.Sleep(time.Second) |
| count++ |
| } |
| |
| close(num) |
| fmt.Println("生产者关闭通道channel.") |
| } |
| |
| func consumer(num chan int, wg *sync.WaitGroup) { |
| defer wg.Done() |
| for val := range num { |
| fmt.Printf("消费者持续接收消息:%d\n", val) |
| time.Sleep(time.Second) |
| } |
| |
| fmt.Println("消费者接收完成.") |
| } |
2.4 互斥同步
| package main |
| |
| import ( |
| "fmt" |
| "time" |
| ) |
| |
| func main() { |
| mu := NewMutexLock() |
| ok := mu.tryLock() |
| fmt.Printf("locked v %v\n", ok) |
| ok = mu.tryLock() |
| fmt.Printf("locked v %v\n", ok) |
| } |
| |
| type mutex struct { |
| ch chan struct{} |
| } |
| |
| func NewMutexLock() *mutex { |
| |
| mu := &mutex{ch: make(chan struct{}, 1)} |
| mu.ch <- struct{}{} |
| return mu |
| } |
| |
| |
| func (m *mutex) lock() { |
| <- m.ch |
| } |
| |
| |
| func (m *mutex) unlock() { |
| select { |
| case m.ch <- struct{}{}: |
| default: |
| panic("unlock of unlocked mutex") |
| } |
| } |
| |
| func (m *mutex) tryLock() bool { |
| select { |
| case <- m.ch: |
| return true |
| default: |
| } |
| |
| return false |
| } |
| |
| |
| func (m *mutex) lockTimeout(timeout time.Duration) bool { |
| timer := time.NewTimer(timeout) |
| select { |
| case <- m.ch: |
| timer.Stop() |
| return true |
| case <- timer.C: |
| } |
| |
| return false |
| } |
| |
| func (m *mutex) isLocked() bool { |
| return len(m.ch) == 0 |
| } |
2.5 信号通知
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "os/signal" |
| "syscall" |
| "time" |
| ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func main() { |
| var ( |
| closing = make(chan struct{}) |
| closed = make(chan struct{}) |
| ) |
| |
| go func() { |
| |
| for { |
| select { |
| case <- closing: |
| return |
| default: |
| fmt.Println("处理业务中...") |
| time.Sleep(100 * time.Millisecond) |
| } |
| } |
| }() |
| |
| |
| terminalChan := make(chan os.Signal) |
| signal.Notify(terminalChan, syscall.SIGINT, syscall.SIGTERM) |
| <- terminalChan |
| |
| close(closing) |
| |
| |
| go doCleanup(closed) |
| |
| |
| select { |
| case <- closed: |
| case <- time.After(time.Second): |
| fmt.Println("超时了,不等cleanup了。") |
| } |
| |
| fmt.Println("优雅退出。") |
| } |
| |
| |
| func doCleanup(closed chan struct{}) { |
| time.Sleep(5 * time.Second) |
| close(closed) |
| } |
2.6 定时器
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "os/signal" |
| "syscall" |
| "time" |
| ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func main() { |
| var ( |
| closing = make(chan struct{}) |
| closed = make(chan struct{}) |
| ) |
| |
| go func() { |
| |
| for { |
| select { |
| case <- closing: |
| return |
| default: |
| fmt.Println("处理业务中...") |
| time.Sleep(100 * time.Millisecond) |
| } |
| } |
| }() |
| |
| |
| terminalChan := make(chan os.Signal) |
| signal.Notify(terminalChan, syscall.SIGINT, syscall.SIGTERM) |
| <- terminalChan |
| |
| close(closing) |
| |
| |
| go doCleanup(closed) |
| |
| |
| select { |
| case <- closed: |
| case <- time.After(time.Second): |
| fmt.Println("超时了,不等cleanup了。") |
| } |
| |
| fmt.Println("优雅退出。") |
| } |
| |
| |
| func doCleanup(closed chan struct{}) { |
| time.Sleep(5 * time.Second) |
| close(closed) |
| } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
2021-07-13 k8s按功能阶段-常用命令
2021-07-13 【linux】ubuntu1604下搭建k8s环境操作流程