在Golang中,Goroutine虽然很好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。所以我们可以限制下Goroutine的数量,这样就需要在每一次执行go之前判断goroutine的数量,如果数量超了,就要阻塞go的执行。第一时间想到的就是使用通道。每次执行的go之前向通道写入值,直到通道满的时候就阻塞了,
```
package main
import "fmt"
var ch chan int
func elegance(){
<-ch
fmt.Println("the ch value receive",ch)
}
func main(){
ch = make(chan int,5)
for i:=0;i<10;i++{
ch <-1
fmt.Println("the ch value send",ch)
go elegance()
fmt.Println("the result i",i)
}
}
```
运行:
```
> go run goroutine.go
the ch value send 0xc00009c000
the result i 0
the ch value send 0xc00009c000
the result i 1
the ch value send 0xc00009c000
the result i 2
the ch value send 0xc00009c000
the result i 3
the ch value send 0xc00009c000
the result i 4
the ch value send 0xc00009c000
the result i 5
the ch value send 0xc00009c000
the ch value receive 0xc00009c000
the result i 6
the ch value receive 0xc00009c000
the ch value send 0xc00009c000
the result i 7
the ch value send 0xc00009c000
the result i 8
the ch value send 0xc00009c000
the result i 9
the ch value send 0xc00009c000
the ch value receive 0xc00009c000
the ch value receive 0xc00009c000
the ch value receive 0xc00009c000
the result i 10
the ch value send 0xc00009c000
the result i 11
the ch value send 0xc00009c000
the result i 12
the ch value send 0xc00009c000
the result i 13
the ch value send 0xc00009c000
the ch value receive 0xc00009c000
the ch value receive 0xc00009c000
the ch value receive 0xc00009c000
the ch value receive 0xc00009c000
the result i 14
the ch value receive 0xc00009c000
```
```
> go run goroutine.go
the ch value send 0xc00007e000
the result i 0
the ch value send 0xc00007e000
the result i 1
the ch value send 0xc00007e000
the result i 2
the ch value send 0xc00007e000
the result i 3
the ch value send 0xc00007e000
the ch value receive 0xc00007e000
the result i 4
the ch value send 0xc00007e000
the ch value receive 0xc00007e000
the result i 5
the ch value send 0xc00007e000
the ch value receive 0xc00007e000
the result i 6
the ch value send 0xc00007e000
the result i 7
the ch value send 0xc00007e000
the ch value receive 0xc00007e000
the ch value receive 0xc00007e000
the ch value receive 0xc00007e000
the result i 8
the ch value send 0xc00007e000
the result i 9
```
这样每次同时运行的goroutine就被限制为5个了。但是新的问题于是就出现了,因为并不是所有的goroutine都执行完了,在main函数退出之后,还有一些goroutine没有执行完就被强制结束了。这个时候我们就需要用到sync.WaitGroup。使用WaitGroup等待所有的goroutine退出。
```
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
// Pool Goroutine Pool
type Pool struct {
queue chan int
wg *sync.WaitGroup
}
// New 新建一个协程池
func NewPool(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()
}
// Wait 等待Goroutine执行完毕
func (p *Pool) Wait(){
p.wg.Wait()
}
func main(){
// 这里限制5个并发
pool := NewPool(5)
fmt.Println("the NumGoroutine begin is:",runtime.NumGoroutine())
for i:=0;i<20;i++{
pool.Add(1)
go func(i int) {
time.Sleep(time.Second)
fmt.Println("the NumGoroutine continue is:",runtime.NumGoroutine())
pool.Done()
}(i)
}
pool.Wait()
fmt.Println("the NumGoroutine done is:",runtime.NumGoroutine())
}
```
运行:
```
the NumGoroutine begin is: 1
the NumGoroutine continue is: 6
the NumGoroutine continue is: 7
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 6
the NumGoroutine continue is: 3
the NumGoroutine continue is: 2
the NumGoroutine done is: 1
```
其中,Go的GOMAXPROCS默认值已经设置为CPU的核数, 这里允许我们的Go程序充分使用机器的每一个CPU,最大程度的提高我们程序的并发性能。runtime.NumGoroutine函数在被调用后,会返回系统中的处于特定状态的Goroutine的数量。这里的特指是指Grunnable\Gruning\Gsyscall\Gwaition。处于这些状态的Groutine即被看做是活跃的或者说正在被调度。
这里需要注意下:垃圾回收所在Groutine的状态也处于这个范围内的话,也会被纳入该计数器。
- 一、经典(一)
- 二、经典(二)
- 三、经典(三)
- 四、经典(四)
- 五、经典(五)
- 六、经典(六)
- 七、经典(七)
- 八、经典(八)
- 九、经典(九)
- 十、经典(十)
- 十一、经典(十一)
- 十二、经典(十二)
- 其他
- 1、知识点一
- 2、面试集
- 3、负载均衡原理
- 4、LVS相关了解
- 5、微服务架构
- 6、分布式锁实现原理
- 7、Etcd怎么实现分布式锁
- 8、Redis的数据结构有哪些,以及实现场景
- 9、Mysql高可用方案有哪些
- 10、Go语言的栈空间管理是怎么样的
- 11、Goroutine和Channel的作用分别是什么
- 12、Go中的锁有哪些?三种锁,读写锁,互斥锁,还有map的安全的锁?
- 13、怎么限制Goroutine的数量
- 14、Goroutine和线程的区别?
- 15、中间件原理