Goroutine是Golang中非常有用的功能,但是在使用中我们经常碰到下面的场景:如果希望等待当前的goroutine执行完成,然后再接着往下执行,该怎么办?本文尝试介绍这类问题的解决方法。
让我们运行下面的代码,并关注输出的结果:
packagemain import( "time" "fmt" ) funcsay(sstring){ fori:=0;i<3;i++{ time.Sleep(100*time.Millisecond) fmt.Println(s) } } funcmain(){ gosay("helloworld") fmt.Println("over!") }输出的结果为:
over!
因为goroutine以非阻塞的方式执行,它们会随着程序(主线程)的结束而消亡,所以程序输出字符串"over!"就退出了,这可不是我们想要的结果。要解决上面的问题,最简单、直接的方式就是通过Sleep函数死等goroutine执行完成:
funcmain(){ gosay("helloworld") time.Sleep(1000*time.Millisecond) fmt.Println("over!") }运行修改后的程序,结果如下:
helloworld
helloworld
helloworld
over!
结果符合预期,但是太low了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!通过channel也可以达到等待goroutine结束的目的,运行下面的代码:
funcmain(){ done:=make(chanbool) gofunc(){ fori:=0;i<3;i++{ time.Sleep(100*time.Millisecond) fmt.Println("helloworld") } done<-true }() <-done fmt.Println("over!") }输出的结果也是:
helloworld
helloworld
helloworld
over!
这种方法的特点是执行多少次done<-true就得执行多少次<-done,所以也不是优雅的解决方式。Golang官方在sync包中提供了WaitGroup类型来解决这个问题。其文档描述如下:
AWaitGroupwaitsforacollectionofgoroutinestofinish.ThemaingoroutinecallsAddtosetthenumberofgoroutinestowaitfor.TheneachofthegoroutinesrunsandcallsDonewhenfinished.Atthesametime,Waitcanbeusedtoblockuntilallgoroutineshavefinished.大意为:WaitGroup用来等待单个或多个goroutines执行结束。在主逻辑中使用WaitGroup的Add方法设置需要等待的goroutines的数量。在每个goroutine执行的函数中,需要调用WaitGroup的Done方法。最后在主逻辑中调用WaitGroup的Wait方法进行阻塞等待,直到所有goroutine执行完成。
使用方法可以总结为下面几点: 创建一个WaitGroup实例,比如名称为:wg 调用wg.Add(n),其中n是等待的goroutine的数量 在每个goroutine运行的函数中执行deferwg.Done() 调用wg.Wait()阻塞主逻辑运行下面的代码:
packagemain import( "time" "fmt" "sync" ) funcmain(){ varwgsync.WaitGroup wg.Add(2) say2("hello",&wg) say2("world",&wg) fmt.Println("over!") } funcsay2(sstring,waitGroup*sync.WaitGroup){ deferwaitGroup.Done() fori:=0;i<3;i++{ fmt.Println(s) } }输出的结果如下:
hello
hello
hello
world
world
world
over!下面是一个稍稍真实一点的例子,检查请求网站的返回状态。如果要在收到所有的结果后进一步处理这些返回状态,就需要等待所有的请求结果返回:
packagemain import( "fmt" "sync" "net/http" ) funcmain(){ varurls=[]string{ "https://www.baidu.com/", "https://www.cnblogs.com/", } varwgsync.WaitGroup for_,url:=rangeurls{ wg.Add(1) gofetch(url,&wg) } wg.Wait() } funcfetch(urlstring,wg*sync.WaitGroup)(string,error){ deferwg.Done() resp,err:=http.Get(url) iferr!=nil{ fmt.Println(err) return"",err } fmt.Println(resp.Status) returnresp.Status,nil }运行上面的代码,输出的结果如下:
200OK
200OK参考:
HowtoWaitforAllGoroutinestoFinishExecutingBeforeContinuing
GoWaitGroupTutorial本文内容总结:没有等待的情况,使用Sleep函数等待,使用channel,标准答案,
原文链接:https://www.cnblogs.com/sparkdev/p/10917536.html