Go语言控制协程(goroutine)的并发数量,有哪些好的解决方法

发布时间 2024-01-07 16:19:03作者: 技术颜良

Go语言控制协程(goroutine)的并发数量,有哪些好的解决方法

磊丰 Go语言圈 2023-12-07 08:31 发表于广东 听全文
Go语言圈
Go语言开发者的学习好助手,分享Go语言知识,技术技巧,学习与交流Go语言开发经验,互动才有助于技术的提升,每天5分钟,助你GO语言技术快乐成长
142篇原创内容
学习与交流:Go语言技术微信群

商务合作加微信:LetsFeng

 

现在就开始你的Go语言学习之旅吧!人生苦短,let’s Go.



Goland 全家桶激活码,免费获取了,赶紧下手

https://web.52shizhan.cn

在使用协程并发处理某些任务时, 其并发数量往往因为各种因素的限制不能无限的增大. 例如网络请求、数据库查询等等。

从运行效率角度考虑,在相关服务可以负载的前提下(限制最大并发数),尽可能高的并发。

在Go语言中,可以使用一些方法来控制协程(goroutine)的并发数量,以防止并发过多导致资源耗尽或性能下降。以下是一些常见的方法:

1. 使用信号量(Semaphore):

可以使用 Go 语言中的 channel 来实现简单的信号量,限制并发数量。

package main

import (
    "fmt"
    "sync"
)

func worker(id int, sem chan struct{}) {
    sem <- struct{}{} // 占用一个信号量
    defer func() {
        <-sem // 释放信号量
    }()

    // 执行工作任务
    fmt.Printf("Worker %d: Working...\n", id)
}

func main() {
    concurrency := 3
    sem := make(chan struct{}, concurrency)
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            worker(id, sem)
        }(i)
    }

    wg.Wait()
    close(sem)
}

在上述例子中,sem 是一个有缓冲的 channel,通过控制 channel 中元素的数量,实现了一个简单的信号量机制。

2. 使用协程池:

可以创建一个固定数量的协程池,将任务分发给这些协程执行。

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d: Working on job %d\n", id, j)
        results <- j * 2
    }
}

func main() {
    const numJobs = 5
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    var wg sync.WaitGroup

    // 启动协程池
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            worker(id, jobs, results)
        }(i)
    }

    // 提交任务
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }

    close(jobs)

    // 等待所有工作完成
    go func() {
        wg.Wait()
        close(results)
    }()

    // 处理结果
    for result := range results {
        fmt.Println("Result:", result)
    }
}

在上述例子中,jobs 通道用于存储任务,results 通道用于存储处理结果。通过创建固定数量的工作协程,可以有效地控制并发数量。

3. 使用 `golang.org/x/sync/semaphore` 包:

Go 1.16 引入了 golang.org/x/sync/semaphore 包,它提供了一个更为灵活的信号量实现。

package main

import (
    "fmt"
    "golang.org/x/sync/semaphore"
    "sync"
)

func worker(id int, sem *semaphore.Weighted) {
    sem.Acquire(semaphore.WithWeight(1))
    defer sem.Release(1)

    // 执行工作任务
    fmt.Printf("Worker %d: Working...\n", id)
}

func main() {
    concurrency := 3
    sem := semaphore.NewWeighted(int64(concurrency))
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            worker(id, sem)
        }(i)
    }

    wg.Wait()
}

在上述例子中,使用 golang.org/x/sync/semaphore 包创建了一个带权重的信号量,控制协程的并发数量。

选择哪种方法取决于具体的应用场景和需求。使用信号量是一种简单而灵活的方法,而协程池则更适用于需要批量处理任务的情况。golang.org/x/sync/semaphore 包提供了一个标准库外的更灵活的信号量实现。

 

文章首发:

 

 

 

 

 

 

更多相关Go语言的技术文章或视频教程,请关注本公众号获取并查看,感谢你的支持与信任!

 

学Go语言哪些事儿 · 目录
上一篇Go语言是如何防范 SQL注入、CSRF、XSS攻击 的下一篇Go语言 字符串拼接方式与性能比较,分析过没?
阅读 1698