golang控制goroutine的几种方式比较
WaitGroup是一种控制并发的方式,它的这种方式是控制多个goroutine同时完成.一个很简单的例子,比如2个goroutine要同时都做完了,才算是完成,先做好的就要等着其他未完成的,所有的goroutine要都全部完成才可以.编程模式是wg.Add()->wg.Done()->wg.Wait()
另外一种是chan通知方式通知其他goroutine说我这个goroutine结束了,编程模式是select->case <- chan
通过Context来通知,编程模式是select->case <- ctx.Done() -> cancel()
Context控制多个goroutine,编程模式…
package main
import (
"time"
"fmt"
"sync"
"context"
)
func main() {
//waitGroup()
//chanSelect()
//ContextSingleGoroutine()
ContextMultiGoroutine()
}
// 使用WaitGroup来控制groutine
func waitGroup(){
var wg sync.WaitGroup
wg.Add(2)
go func() {
time.Sleep(2*time.Second)
fmt.Println("1号员工完成")
wg.Done()
}()
go func() {
time.Sleep(2*time.Second)
fmt.Println("2号员工完成")
wg.Done()
}()
wg.Wait()
fmt.Println("好了,所有员工都做完了,收工")
}
// 使用select case:chan来控制goroutine
func chanSelect(){
stop:=make(chan bool)
go func() {
for {
select {
case<-stop:
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2*time.Second)
}
}
}()
time.Sleep(10*time.Second)
fmt.Println("可以了,通知监控停止")
stop<-true
// 为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5*time.Second)
}
// 使用Context来控制goroutine
// Context控制单个goroutine
func ContextSingleGoroutine(){
// context.Background() 返回一个空的Context,这个空的Context一般用于整个Context树的根节点。
// 然后我们使用context.WithCancel(parent)函数,创建一个可取消的子Context,然后当作参数传给
// goroutine使用,这样就可以使用这个子Context跟踪这个goroutine。
ctx,cancel:=context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
// 在goroutine中,使用select调用<-ctx.Done()判断是否要结束,如果接受到值的话,
// 就可以返回结束goroutine了;如果接收不到,就会继续进行监控。
select {
case <- ctx.Done():
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2*time.Second)
}
}
}(ctx)
time.Sleep(10*time.Second)
fmt.Println("可以了,通知监控停止")
// 那么是如何发送结束指令的呢?这就是示例中的cancel函数啦,它是我们调用context.WithCancel(parent)函数生成子Context的
// 时候返回的,第二个返回值就是这个取消函数,它是CancelFunc类型的。我们调用它就可以发出取消指令,然后我们的监控goroutine就
// 会收到信号,就会返回结束。
cancel()
// 为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5*time.Second)
}
// Context控制多个goroutine
func ContextMultiGoroutine() {
ctx,cancel:=context.WithCancel(context.Background())
// 示例中启动了3个监控goroutine进行不断的监控,每一个都使用了Context进行跟踪,当我们使用cancel函数通知取消时,这3个goroutine都会
// 被结束。这就是Context的控制能力,它就像一个控制器一样,按下开关后,所有基于这个Context或者衍生的子Context都会收到通知,这时就可以
// 进行清理操作了,最终释放goroutine,这就优雅的解决了goroutine启动后不可控的问题。
go watch(ctx,"【监控1】")
go watch(ctx,"【监控2】")
go watch(ctx,"【监控3】")
time.Sleep(10*time.Second)
fmt.Println("可以了,通知监控停止")
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5*time.Second)
}
func watch(ctx context.Context,name string){
for {
select {
case <- ctx.Done():
fmt.Println(name,"监控退出,停止了...")
return
default:
fmt.Println(name,"goroutine监控中...")
time.Sleep(2*time.Second)
}
}
}