[场景实战]golang channel 在实际项目中的典型应用举例

通过之前的文章,想必大家都已了解了Channel 是 Go 语言并发编程的核心工具,通过合理设计 Channel 的结构和使用方式,可以高效解决多线程协作中的通信、同步和资源管理问题。本文主要讲述 Go 语言中 Channel 在实际项目中的典型应用 示例,涵盖生产者-消费者、任务队列、事件通知、限流等场景:

1. 生产者-消费者模式

场景:多个生产者生成数据,多个消费者处理数据,通过 Channel 实现解耦和同步。

package main

import (
    "fmt"
    "sync"
    "time"
)

func producer(id int, ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        fmt.Printf("Producer %d produced: %d\n", id, i)
        ch <- i // 发送数据到 Channel
        time.Sleep(time.Millisecond * 100)
    }
}

func consumer(id int, ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for num := range ch { // 从 Channel 读取数据
        fmt.Printf("Consumer %d consumed: %d\n", id, num)
        time.Sleep(time.Millisecond * 300) // 模拟处理时间
    }
}

func main() {
    ch := make(chan int, 10) // 带缓冲的 Channel
    var wg sync.WaitGroup

    // 启动 2 个生产者
    for i := 1; i <= 2; i++ {
        wg.Add(1)
        go producer(i, ch, &wg)
    }

    // 启动 3 个消费者
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go consumer(i, ch, &wg)
    }

    wg.Wait()
    close(ch) // 关闭 Channel,避免消费者阻塞
}

关键点

  • Channel 缓冲区(make(chan int, 10))解耦生产者和消费者的速度差异。
  • sync.WaitGroup 确保所有 goroutine 完成后再关闭 Channel。
  • range ch 会自动停止读取,当 Channel 被关闭且无数据时。

2. 任务队列与协程池

场景:限制并发数量,避免资源耗尽(如数据库连接、HTTP 请求)。

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, taskChan <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for task := range taskChan {
        fmt.Printf("Worker %d processing task %d\n", id, task)
        time.Sleep(time.Second) // 模拟任务处理时间
    }
}

func main() {
    taskChan := make(chan int, 100) // 任务队列
    var wg sync.WaitGroup

    // 创建 3 个协程池中的工作 goroutine
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, taskChan, &wg)
    }

    // 提交 10 个任务
    for i := 1; i <= 10; i++ {
        taskChan <- i
    }

    close(taskChan) // 关闭任务队列,通知所有 worker 退出
    wg.Wait()
}

关键点

  • Channel 作为任务队列,协程池大小为 3,限制并发数量。
  • close(taskChan) 广播通知所有 worker 停止处理任务。

3. 事件通知与优雅关闭

场景:通过关闭 Channel 通知所有监听者退出,实现服务的优雅关闭。

package main

import (
    "fmt"
    "time"
)

func monitor(done <-chan struct{}) {
    for {
        select {
        case <-done:
            fmt.Println("Monitor: Stopped.")
            return
        default:
            fmt.Println("Monitor: Doing work...")
            time.Sleep(time.Second)
        }
    }
}

func main() {
    done := make(chan struct{}) // 事件通知 Channel
    go monitor(done)

    time.Sleep(5 * time.Second) // 模拟服务运行
    close(done)                 // 关闭 Channel,通知 monitor 退出
    fmt.Println("Main: Shutdown complete.")
}

关键点

  • struct{} 类型的 Channel 用于通知,不传递数据(节省内存)。
  • close(done) 广播通知所有监听者(如 monitor)退出。

4. 超时控制与 select 语句

场景:为 Channel 操作设置超时,避免长时间阻塞。

package main

import (
    "fmt"
    "time"
)

func longTask(ch chan<- string) {
    time.Sleep(3 * time.Second) // 模拟长时间任务
    ch <- "Task completed"
}

func main() {
    ch := make(chan string)
    go longTask(ch)

    select {
    case result := <-ch:
        fmt.Println("Result:", result)
    case <-time.After(2 * time.Second): // 设置 2 秒超时
        fmt.Println("Timeout: Task took too long")
    }
}

关键点

  • select 语句结合 time.After 实现超时控制。
  • 若任务超过 2 秒未完成,触发超时分支。

5. 单向 Channel 的应用

场景:限制 Channel 的使用方向,提高代码安全性。

package main

import "fmt"

// 只写 Channel
func producer(ch chan<- int) {
    ch <- 42
    // ch <- 43 // 错误:无法从只写 Channel 读取
}

// 只读 Channel
func consumer(ch <-chan int) {
    fmt.Println(<-ch)
    // ch <- 100 // 错误:无法向只读 Channel 写入
}

func main() {
    ch := make(chan int)
    go producer(ch)
    go consumer(ch)
    time.Sleep(time.Second)
}

关键点

  • 单向 Channel(chan<- int / <-chan int)限制函数参数的行为。
  • 避免误操作(如向只读 Channel 写入数据)。

6. 限流(Rate Limiting)

场景:通过 Channel 控制并发请求速率,防止资源过载。

package main

import (
    "fmt"
    "time"
)

func rateLimitedTask(id int, tokenChan <-chan struct{}) {
    <-tokenChan // 等待令牌
    fmt.Printf("Task %d started\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Task %d completed\n", id)
}

func main() {
    tokenChan := make(chan struct{}, 2) // 最多允许 2 个并发任务
    for i := 0; i < 5; i++ {
        tokenChan <- struct{}{} // 填充令牌
        go rateLimitedTask(i, tokenChan)
    }
    time.Sleep(5 * time.Second)
}

关键点

  • 使用带缓冲的 Channel(tokenChan)作为令牌桶。
  • 每个任务必须获取一个令牌(<-tokenChan)才能执行,从而限制并发数。

总结

场景Channel 的作用
生产者-消费者解耦生产与消费速度,缓冲数据
协程池限制并发数量,复用 goroutine
事件通知通过关闭 Channel 广播信号,优雅终止服务
超时控制结合 selecttime.After 处理超时
单向 Channel限制 Channel 的使用方向,提高代码安全性
限流通过缓冲 Channel 控制资源访问速率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值