> 文章列表 > 初学者Go并发(3)

初学者Go并发(3)

初学者Go并发(3)

3、worker pool(goroutine池)

在工作中我们通常使用指定启动得goroutine数量-Worker pool模式,来控制goroutine得数量,防止goroutine泄露和暴涨

实例代码如下:

package mainimport ("fmt""time"
)func worker(id int, jobs <-chan int, results chan<- int) {for j := range jobs {fmt.Printf("worker:%d start job:%d", id, j)time.Sleep(time.Second)fmt.Printf("worker:%d end job:%d", id, j)results <- j * 2}
}func main() {jobs := make(chan int, 100)results := make(chan int, 100)//然后开启3个goroutinefor i := 1; i < 3; i++ {go worker(i, jobs, results)}//5个任务for j := 1; j <= 5; j++ {jobs <- j}close(jobs)//输出结果for a := 1; a < 6; a++ {<-results}
}

4、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:默认操作
}

看如下例子进行相关的演示

func main(){ch:=make(chan int)for i:=0;i<100;i++{select{case x:=<=ch:fmt.Println(x)case ch<-i:}}
}

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

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