文档章节

如何优雅的控制goroutine的数量

borey
 borey
发布于 2016/05/27 15:44
字数 448
阅读 6466
收藏 75

1,为什么要控制goroutine的数量?     goroutine固然好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。比如:

for i:=0; i < 10000; i++ {
    go work()
}

2,用什么方法控制goroutine的数量? 要在每一次执行go之前判断goroutine的数量,如果数量超了,就要阻塞go的执行。第一时间想到的就是使用通道。每次执行的go之前向通道写入值,直到通道满的时候就阻塞了,如下:

var ch chan int

func work() {
    //do something
    <-ch
}

func main() {
    ch = make(chan int, 10)
    for i:=0; i < 10000; i++ {
       ch <- 1
       go work()
    }
}

这样每次同时运行的goroutine就被限制为10个了。但是新的问题出现了,因为并不是所有的goroutine都执行完了,在main函数退出之后,还有一些goroutine没有执行完就被强制结束了。这个时候我们就需要用到sync.WaitGroup。使用WaitGroup等待所有的goroutine退出。如下:

var wg *sync.WaitGroup

func work() {
    defer wg.Done()
    //do something
}

func main() {
    wg = &sync.WaitGroup{}
    for i:=0; i < 10000; i++ {
       wg.Add(1)
       go work()
    }
    wg.Wait()//等待所有goroutine退出
}

3,优雅的使用并控制goroutine的数量 综上所述,我们封装一下,代码如下:

package gpool

import (
	"sync"
)

type pool struct {
	queue chan int
	wg    *sync.WaitGroup
}

func New(size int) *pool {
	if size <= 0 {
		size = 1
	}
	return &pool{
		queue: make(chan int, size),
		wg:    &sync.WaitGroup{},
	}
}

func (p *pool) Add(delta int) {
	for i := 0; i < delta; i++ {
		p.queue <- 1
	}
	for i := 0; i > delta; i-- {
		<-p.queue
	}
	p.wg.Add(delta)
}

func (p *pool) Done() {
	<-p.queue
	p.wg.Done()
}

func (p *pool) Wait() {
	p.wg.Wait()
}

来段测试代码:

package gpool_test

import (
	"runtime"
	"testing"
	"time"
	"gpool"
)

func Test_Example(t *testing.T) {
	pool := gpool.New(100)
	println(runtime.NumGoroutine())
	for i := 0; i < 1000; i++ {
		pool.Add(1)
		go func() {
			time.Sleep(time.Second)
			println(runtime.NumGoroutine())
			pool.Done()
		}()
	}
	pool.Wait()
	println(runtime.NumGoroutine())
}

good job,Over~

© 著作权归作者所有

borey
粉丝 28
博文 55
码字总数 31182
作品 0
深圳
程序员
私信 提问
加载中

评论(11)

L
LucasYe
那怎么得出"最合适"的goroutine数量呢
borey
borey 博主

引用来自“sheepbao”的评论

最优雅的是用channel缓冲

我只想知道为什么标准库sync.WaitGroup不提供这个功能。难道是一次只做一件事么。
sheepbao
sheepbao
最优雅的是用channel缓冲
j
jirodobiw
2可以的
borey
borey 博主

引用来自“borey”的评论

引用来自“lubia”的评论

很多时候的需求是有1万个请求,但我只能开100个goroutine,需要控制同时运行的数量,怎么破?

这个就可以啊,New(100)就是最多开100个routine

同时最多开100个,一个routine结束就可以新开一个。接受请求的时候就直接开就可以了。buffer chan满了就会阻塞。
borey
borey 博主

引用来自“lubia”的评论

很多时候的需求是有1万个请求,但我只能开100个goroutine,需要控制同时运行的数量,怎么破?

这个就可以啊,New(100)就是最多开100个routine
lubia
lubia
很多时候的需求是有1万个请求,但我只能开100个goroutine,需要控制同时运行的数量,怎么破?
borey
borey 博主

引用来自“hell0cat”的评论

纯channel可以实现的,不需要再依赖sync同步机制,比如我都是这样用并发机制的:
http://www.oschina.net/code/snippet_170216_25349
可以对你的这个例子用我的方法改造一下,很简单的测试不同数量的goroutine的效果如何
borey
borey 博主

引用来自“hell0cat”的评论

纯channel可以实现的,不需要再依赖sync同步机制,比如我都是这样用并发机制的:
http://www.oschina.net/code/snippet_170216_25349
可以的,不过使用起来不直观 pool := gpool.New(100) pool.Add(1) go func() { defer pool.Done() }() pool.Wait() 你也可以不用sync来实现封装
hell0cat
hell0cat
纯channel可以实现的,不需要再依赖sync同步机制,比如我都是这样用并发机制的:
http://www.oschina.net/code/snippet_170216_25349
mongodb: 关于Mongoose的geoNear方法的使用

文章: Selenium自动化测试LOGO(临时文章) mongodb: 关于Mongoose的geoNear方法的使用 Protobuf在go和java数据交互 每日一博 | 如何优雅的控制goroutine的数量 sdk: 被Google Play下架刷爆朋...

d_watson
2016/05/28
61
0
Goroutine + Channel 实践

goroutine不同于thread,threads是操作系统中的对于一个独立运行实例的描述,不同操作系统,对于thread的实现也不尽相同;但是,操作系统并不知道goroutine的存在,goroutine的调度是有Golan...

黑神领主
2016/12/10
121
0
Go 并发模型:管道和取消

简介 Golang的原子并发特性使得它很容易构造流数据管道,这使得Golang可有效的使用I/O和多CPU特性。本文提出一些关于管道的示例,在这个过程中突出了操作失败的微妙之处和介绍处理失败的具体...

oschina
2014/03/14
2.8K
5
go package学习——runtime

package runtime主要是与go的runtime系统进行互动操作,例如控制goroutine的函数等。它也包含reflect package所需的低等级信息。 1. Environment Variables GOGC: 设置初始的垃圾回收百分比。...

加油2018
2013/09/04
2.6K
0
Go基础系列:WaitGroup用法说明

正常情况下,新激活的goroutine的结束过程是不可控制的,唯一可以保证终止goroutine的行为是main goroutine的终止。也就是说,我们并不知道哪个goroutine什么时候结束。 但很多情况下,我们正...

echojson
04/18
1
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 我,小小编辑,食人族酋长

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @宇辰OSC :分享娃娃的单曲《飘洋过海来看你》: #今日歌曲推荐# 《飘洋过海来看你》- 娃娃 手机党少年们想听歌,请使劲儿戳(这里) @宇辰OSC...

小小编辑
今天
405
10
MongoDB系列-- SpringBoot 中对 MongoDB 的 基本操作

SpringBoot 中对 MongoDB 的 基本操作 Database 库的创建 首先 在MongoDB 操作客户端 Robo 3T 中 创建数据库: 增加用户User: 创建 Collections 集合(类似mysql 中的 表): 后面我们大部分都...

TcWong
今天
4
0
spring cloud

一、从面试题入手 1.1、什么事微服务 1.2、微服务之间如何独立通讯的 1.3、springCloud和Dubbo有哪些区别 1.通信机制:DUbbo基于RPC远程过程调用;微服务cloud基于http restFUL API 1.4、spr...

榴莲黑芝麻糊
今天
3
0
Executor线程池原理与源码解读

线程池为线程生命周期的开销和资源不足问题提供了解决方 案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。 线程实现方式 Thread、Runnable、Callable //实现Runnable接口的...

小强的进阶之路
昨天
7
0
maven 环境隔离

解决问题 即 在 resource 文件夹下面 ,新增对应的资源配置文件夹,对应 开发,测试,生产的不同的配置内容 <resources> <resource> <directory>src/main/resources.${deplo......

之渊
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部