Golang简单的控制并发数的例子|周末学习

667 阅读2分钟

本文已参与 周末学习计划,点击查看详情

  1. 想象一个场景,现在有10个任务,希望能够并行执行。很简单对不对,直接使用协程go出去。
package main

func main() {
	someUnhandledItems := []int64{1,2,3,4,5,6,7,8,9,10}

        for _, i := range someUnhandledItems {
                go handleItem(i)
	}
}

func handleItem(item int64) {
	log.Println("item is : ", item)
}
  1. 这里执行一下就会发现问题,为什么控制台没有输出?原因其实是因为main函数其实也是一个协程,由于在handleItem函数执行完之前main函数已经退出了,所以go出去的函数没有拿到返回。可以用waitgroup来解决。
package main

import"fmt"
        "sync"var wg sync.WaitGroup

func main() {
	someUnhandledItems := []int64{1,2,3,4,5,6,7,8,9,10}
	for _, i := range someUnhandledItems {
                wg.Add(1)
		go handleItem(i)
	}
	wg.Wait()
}

func handleItem(item int64) {
	defer wg.Done()
	log.Println("item is : ", item)
}
  1. 这次可以拿到结果了。但是还有一个需求,要求一次最多执行3个任务,即并发数是3,要怎么解决呢,可以使用channel。
package main

import"fmt"
        "sync"var wg sync.WaitGroup
var maxConcur = 3

func main() {
	ch := make(chan struct{}, maxConcur)
	someUnhandledItems := []int64{1,2,3,4,5,6,7,8,9,10}
	for _, i := range someUnhandledItems {
                wg.Add(1)
		ch <- struct{}{}
		go handleItem(i,ch)
	}
	wg.Wait()
}

func handleItem(item int64,ch chan struct{}) {
	defer wg.Done()
	log.Println("item is : ", item)
	<- ch
}

嗯,写完啦!似乎可以满足要求。但是再仔细看看呢?看不出来的话运行下试试吧。 发现问题了吧,似乎哪里不对劲。输出总数好像不是10. 仔细推演一下,就发现问题了吧,wg.Add(1)的位置对吗?不对的话怎么修改呢?

package main

import"fmt"
        "sync"var wg sync.WaitGroup
var maxConcur = 3

func main() {
	ch := make(chan struct{}, maxConcur)
	someUnhandledItems := []int64{1,2,3,4,5,6,7,8,9,10}
	for _, i := range someUnhandledItems {
		ch <- struct{}{}
		go handleItem(i,ch)
	}
	wg.Wait()
}

func handleItem(item int64,ch chan struct{}) {
        wg.Add(1)
	defer wg.Done()
	log.Println("item is : ", item)
	<- ch
}

这样就没问题啦。 再问一次,真的没问题了吗? 如果handleItem是一个非常复杂的函数,是不是应该在返回中添加error?如果handleItem发生了panic的话,可以捕捉到吗?上面提出的两个问题需要怎么解决? 欢迎大家讨论,明天见啦❤️