【Go】 worker pool(goroutine池)和

Worker pool(goroutine池)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
go复制代码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
}
}

输出结果如下:

1
2
3
4
5
text复制代码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多路复用

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

1
2
3
4
5
6
7
go复制代码for{
   // 尝试从ch1接收值
   data, ok := <-ch1
   // 尝试从ch2接收值
   data, ok := <-ch2
   …
}

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

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

1
2
3
4
5
6
7
8
9
10
go复制代码select{
   case <-ch1:
       ...
   case data := <-ch2:
       ...
   case ch3<-data:
       ...
   default:
       默认操作
}

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

1
2
3
4
5
6
7
8
9
10
11
go复制代码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函数。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%