Golang 入门 : 等待 goroutine 完成任务
本文内容纲要:
-没有等待的情况
-使用Sleep函数等待
-使用channel
-标准答案
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函数等待
要解决上面的问题,最简单、直接的方式就是通过Sleep函数死等goroutine执行完成:
funcmain(){
gosay("helloworld")
time.Sleep(1000*time.Millisecond)
fmt.Println("over!")
}
运行修改后的程序,结果如下:
helloworld
helloworld
helloworld
over!
结果符合预期,但是太low了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!
使用channel
通过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