[Go] worker pool(goroutine池)和 select 多路复用

·  阅读 223

Worker pool(goroutine池)

在工作中我们通常会使用可以指定启动的goroutine数量–worker pool模式,控制goroutine的数量,防止goroutine泄漏和暴涨。

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)
    // 开启3个goroutine
    for id:=0; id<3; id++ {
        go worker(id, jobs, results)
    }

    // 生成5个job
    for num:=0; num<5; num++ {
        jobs <- num
    }
    close(jobs)

    // 输出结果
    for a := 0; a < 5; a++ {
        <-results
    }
    
    
    // 死锁,只有 close(results) 可用
    //for  {
    // x,ok := <- results
    // if !ok {
    //    break
    // }
    // fmt.Println(x)
    //}
    // 死锁,只有 close(results) 可用
    //for x := range results {
    // fmt.Println(x)
    //}

}

func worker(id int, jobs <-chan int, results chan<- int)  {
    for j := range jobs {
        time.Sleep(time.Second)
        fmt.Printf("JobID:{%d} Job is:%d \n", id, j)
        results <- j * 2
    }
}
复制代码

输出结果如下:

JobID:{0} Job is:0
JobID:{2} Job is:2
JobID:{1} Job is:1
JobID:{2} Job is:4
JobID:{0} Job is:3
复制代码

Select多路复用

在某些场景下我们需要同时从多个通道接收数据。通道在接收数据时,如果没有数据可以接收将会发生阻塞。你也许会写出如下代码使用遍历的方式来实现:

for{
    // 尝试从ch1接收值
    data, ok := <-ch1
    // 尝试从ch2接收值
    data, ok := <-ch2
    …
}
复制代码

这种方式虽然可以实现从多个通道接收值的需求,但是运行性能会差很多。为了应对这种场景,Go内置了select关键字,可以同时响应多个通道的操作。

select的使用类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的语句。具体格式如下:

select{
    case <-ch1:
        ...
    case data := <-ch2:
        ...
    case ch3<-data:
        ...
    default:
        默认操作
}
复制代码

举个小例子来演示下select的使用:

func main() {
    ch := make(chan int, 1)
    for i := 0; i < 10; i++ {
        select {
            case x := <-ch:
                fmt.Println(x)
            case ch <- i:
                fmt.Printf("%d放进去了\n",i)
        }
    }
}
复制代码

使用select语句能提高代码的可读性。

  • 可处理一个或多个channel的发送/接收操作。
  • 如果多个case同时满足,select会随机选择一个。
  • 对于没有caseselect{}会一直等待,可用于阻塞main函数。
分类:
后端
标签:
分类:
后端
标签: