go语言之使用channel实现协程池管理代码示例

2023-06-01 00:00:00 语言 代码 示例

channel主要是为了实现go的并发特性,用于并发通信的,也就是在不同的协程单元goroutine之间同步通信。

默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock。


使用 channel 实现协程池

通过 Channel 实现 Goroutine Pool, 缺点是会造成协程的频繁开辟和注销,但好在简单灵活通用。


示例代码:

package main
import (
    "fmt"
    "io/ioutil"
    "net/http"
    "sync"
)
// Pool goroutine Pool
type Pool struct {
    queue chan int
    wg    *sync.WaitGroup
}
// New 新建一个协程池
func New(size int) *Pool {
    if size <= 0 {
        size = 1
    }
    return &Pool{
        queue: make(chan int, size),
        wg:    &sync.WaitGroup{},
    }
}
// Add 新增一个执行
func (p *Pool) Add(delta int) {
    // delta为正数就添加
    for i := 0; i < delta; i++ {
        p.queue <- 1
    }
    // delta为负数就减少
    for i := 0; i > delta; i-- {
        <-p.queue
    }
    p.wg.Add(delta)
}
// Done 执行完成减一
func (p *Pool) Done() {
    <-p.queue
    p.wg.Done()
}
func (p *Pool) Wait() {
    p.wg.Wait()
}
func main() {
    // 这里限制100个并发
    pool := New(100) // sync.WaitGroup{}
    //假设需要发送1000万个http请求,然后我并发100个协程取完成这件事
    for i := 0; i < 10000000; i++ {
        pool.Add(1) //发现已存在100个人正在发了,那么就会卡住,直到有人完成了宣布自己退出协程了
        go func(i int) {
            resp, err := http.Get("https://www.baidu.com")
            if err != nil {
                fmt.Println(i, err)
            } else {
                defer resp.Body.Close()
                result, _ := ioutil.ReadAll(resp.Body)
                fmt.Println(i, string(result))
            }
            pool.Done()
        }(i)
    }
    pool.Wait()
}


相关文章