Go 限制并发、限速
Go的并发使用起来非常容易,随意就起个goroutine。但是日常开发中,考虑到机器CPU、内存、文件描述符、网络以及下游中间件性能等等的影响,通常不会无脑使用并发。
buffered channel如果满了,再塞入则会阻塞,直到有释放才行。下面例子,就是利用这个机制。
限制并发1
这个例子展示了,同一时刻,确保最多只有一定数量的goroutine运行。
var wg sync.WaitGroup
const MAX_NUM = 5
func main() {
var works []string
for i := 'a'; i < 'a'+26; i++ {
works = append(works, string(i))
}
// 一个容量是 MAX_NUM 的channel
restricted := make(chan struct{}, MAX_NUM)
wg.Add(len(works))
for _, w := range works {
go doIt(restricted, w)
}
wg.Wait()
}
func doIt(ch chan struct{}, url string) {
// 进入工作函数后,向通道塞入一个元素,如果是满的则会阻塞,直到有位置。
// 保证同一时刻最多只有 MAX_NUM 个goroutine运行
ch <- struct{}{}
// 结束后释放
defer func() {
<-ch
}()
// 工作
time.Sleep(time.Second)
fmt.Printf("do :%s\n", url)
wg.Done()
}
限制并发2
这个例子展示,m个goroutine干n个活儿。
const (
goroutinesNum = 5
tasksNum = 20
)
var wg sync.WaitGroup
func init() {
rand.Seed(time.Now().Unix())
}
func main() {
worksCh := make(chan string, tasksNum)
// 模拟n个活儿
for i := 0; i < tasksNum; i++ {
worksCh <- fmt.Sprintf("work %d", i)
}
// todo close channel
close(worksCh)
wg.Add(goroutinesNum)
for i := 0; i < goroutinesNum; i++ {
go doSth(i, worksCh)
}
wg.Wait()
}
func doSth(num int, worksCh chan string) {
defer wg.Done()
for w := range worksCh {
// 干活
time.Sleep(time.Millisecond * time.Duration(rand.Int63n(1000)))
fmt.Printf("goNo. %d, done %s\n", num, w)
}
}