使用 channel 控制并发数量

协程

goroutine 是轻量级线程,调度由 Go 运行时进行管理的。Go 语言的并发控制主要使用关键字 go 开启协程 goroutine。Go 协程(Goroutine)之间通过信道(channel)进行通信,简单的说就是多个协程之间通信的管道。信道可以防止多个协程访问共享内存时发生资源争抢的问题。语法格式:

// 普通函数创建 goroutine
go 函数名(参数列表)

//匿名函数创建 goroutine
go func(参数列表){
    //函数体
}(调用参数列表)
复制代码

协程可以开启多少个?是否有限制呢?

func testRoutine() {
    var wg sync.WaitGroup
    for i := 0; i < math.MaxInt32; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Printf("并发数量:%d/n", i)
            time.Sleep(time.Second)
        }(i)
    }
    wg.Wait()
}
复制代码

以上代码开启了 math.MaxInt32个协程的并发,执行后可以看到结果直接 panic:“panic: too many concurrent operations on a single file or socket (max 1048575)”。整个并发操作超出了系统最大值。

控制协程数量

sync 同步机制

使用 sync.WaitGroup 启动指定数量的协程 goroutine。

func testRoutine() {
    var wg = sync.WaitGroup{}

    taskCount := 5 // 指定并发数量
    for i := 0; i < taskCount; i++ {
        wg.Add(1)
        go func(i int) {
            fmt.Println("go func ", i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}
复制代码

如果 taskcount 设置的很大超出了限制的,则其还是没有控制到并发数量。可以优化下设计,类似池的设计思想,通过允许最大连接数控制量,当超出了数量就需要等待释放,有空闲的连接的时候才可以继续执行。

func testRoutine() {
    task_chan := make(chan bool, 3) //100 为 channel长度
    wg := sync.WaitGroup{}
    defer close(task_chan)
    for i := 0; i < math.MaxInt; i++ {
        wg.Add(1)
        fmt.Println("go func ", i)
        task_chan <- true
        go func() {
                <-task_chan
                defer wg.Done()
        }()
    }

    wg.Wait()
}
复制代码
  • 创建缓冲区大小为 3 的 channel,在没有被接收的情况下,至多发送 3 个消息则被阻塞。通过 channel 控制每次并发的数量。
  • 开启协程前,设置 task_chan <- true,若缓存区满了则阻塞
  • 协程任务执行完成后就释放缓冲区
  • 等待所有的并发都处理结束后则函数结束。其实可以不使用 sync.WaitGroup。因使用 channel 控制并发处理的任务数量可以不用使用等待并发处理结束。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
是的,可以通过使用 channel控制并发 goroutine 的数量。具体的实现方法是,创建一个带缓冲的 channel,并在启动 goroutine 之前将一些数据发送到 channel 中。每当一个 goroutine 启动时,从 channel 中读取一个数据,如果 channel 已经空了,则 goroutine 会被阻塞,直到有新的数据进入 channel。 这个方法的原理是,通过限制 channel 中数据的数量,从而限制同时运行的 goroutine 数量。当 channel 已满时,新的 goroutine 会被阻塞,直到有其他 goroutine 执行完毕,从而腾出一个 channel 位置。 下面是一个示例代码,使用 channel 控制同时运行的 goroutine 数量: ```go package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "started job", j) time.Sleep(time.Second) fmt.Println("worker", id, "finished job", j) results <- j * 2 } } func main() { const numJobs = 10 const numWorkers = 3 jobs := make(chan int, numJobs) results := make(chan int, numJobs) for w := 1; w <= numWorkers; w++ { go worker(w, jobs, results) } for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) for a := 1; a <= numJobs; a++ { <-results } } ``` 在这个示例代码中,我们创建了一个 `jobs` channel 和一个 `results` channel,并启动了三个 goroutine 来处理 `jobs` channel 中的任务。在 `main` 函数中,我们向 `jobs` channel 中发送了 10 个任务,并在最后等待所有任务完成。 可以看到,在启动 goroutine 时,我们使用了带缓冲的 `jobs` channel,来限制同时运行的 goroutine 数量。当 `jobs` channel 已满时,新的 goroutine 会被阻塞,直到有其他 goroutine 执行完毕,从而腾出一个 channel 位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码云笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值