+8613426109659
webmaster@21cto.com

如何使用Go的并行做更多事情

图片
计算机擅长快速完成很多事情。非常多的事情,而且速度也非常快。

例如,这里有一个Go的函数,它会休眠一秒钟:

func doSomething(){    time.Sleep(time.Second)}

做很多事情


当你想做某事时,你可以这样做:

func doManyThings(int){    for range n{            do Something()    } }  func main(){     doManyThings(10) }

但是做这件事需要多长时间呢?比如说十次?

$ time go run main.goreal    0m10.256s

果然不出所料,大约10秒……如果我们想重复一百万次怎么办?那至少需要一百万秒,也就是将近十几天才行。

等等,这是怎么回事?计算机应该很快才对啊!

快速完成许多事情


但是,我们可以通过编程让它们并发执行任务,而不是像上面那样顺序执行(一个接一个的),从而提高它们的速度。

如果你有多个 CPU(现在可以肯定都有),这些任务就会并行执行(这绝对是人类不应该尝试模拟的——想了解更多,你可以阅读约翰·哈里(Johann Hari )的《丢失焦点(Lost Focus)》一书)!

你可以通过以下这样生成新的goroutines,来同时运行 Go 中的许多事情:

func doManyThings(int){  var wgsync.WaitGroup// create concurrency-safe counter  for range n{wg.Add(1)// increment counter by one    gofunc(){// launch function on new goroutine and continuedefer     wg.Done()// decrement counter on return from function     do Something()  }()}wg.Wait()// wait till counter is 0 (all functions are done)}func main(){do ManyThings(1e6)}

那么,现在需要多长时间才能完成一百万次只执行一秒钟的事情呢?它在我的笔记本电脑上,不到三秒钟:

$ time go run main.goreal    0m2.667s

看到了没,这可比原来的十二天快多了!

做很多事情,快速产生输出


在执行某件事时,我们经常希望看到一些输出。至少,我们想知道任务是否顺利完成:


// Now doSomething returns an error value. It's nil when all is good.func doSomething()error{    time.Sleep(time.Second)    if rand.Intn(100)==0{// circa one percent of tasks will fail        return errors.New("we got a problem")    }    return nil}

那么,我们如何获取在 goroutine 上并发执行的任务的输出呢?为此可以使用Channels

func doManyThings(n int)[]error{    ch:=make(chanerror,n)// create channel of errors that can hold n items        for range n{    gofunc(){           ch<-doSomething()// send function's output to channel}()}var errs[]error for range n{    err:=<-ch// receive value from channel and assign it to err        if err!=nil{            errs=append(errs,err)        }}return errs}func main(){    n:=1_000_000    errs:=doManyThings(n)    fmt.Printf("Doing %d things there where %d errors.\n",n,len(errs))}

如各位所见,我们不再需要counter计数器了。

这是因为我们再次循环(通过)来接收来自Channel的所有值。第二个循环实际上等待所有 Goroutine 完成——每个 Goroutine 只向通道发送一个值,因此通过Channel读取值,我们能够知道所有Goroutine 都已完成了工作。

缓冲通道(具有一定容量)确保所有 Goroutine 都可以无阻塞地发送结果,即使我们尚未开始从Channel读取数据。

作者:洛逸

参考地址:

https://github.com/go-monk/doing-many-things-in-parallel

评论

我要赞赏作者

请扫描二维码,使用微信支付哦。