golang 常用控制协程的三种方式
golang的协程是没有相互的依赖关系的
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("此处为父协程")
go func() {
for {
time.Sleep(time.Second * 2)
fmt.Println("此处为子协程")
}
}()
defer fmt.Println("父协程执行完毕")
return
}()
time.Sleep(time.Second * 10)
fmt.Println("exit")
}
执行上面的代码就可以看出golang里,协程都是互相独立的,没有依赖(父子)关系。main函数本身也运行在一个goroutine中,main是所有协程的被依赖者是一个特例,所以我们就需要对我们创造的协程进行控制,常用到的协程控制有以下三种方式
waitGroup
waitGroup这种方式适用于一个任务可以被拆分成几个子任务,并且子任务之间的关联程度不高,全部的子任务都完成,才会进行下一阶段的任务。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
time.Sleep(time.Second * 1)
fmt.Println("任务1执行完成")
}()
go func() {
defer wg.Done()
time.Sleep(time.Second * 2)
fmt.Println("任务2执行完成")
}()
go func() {
defer wg.Done()
time.Sleep(time.Second * 3)
fmt.Println("任务3执行完成")
}()
wg.Wait()
fmt.Println("全部任务执行完成")
}
channel
当满足某一种需要,通知协程不再继续工作的时候,就需要用到channel,channel主要应用于协程之间的通讯
package main
import (
"fmt"
"time"
)
func main() {
var signal = make(chan struct{})
go func() {
for {
select {
case <-signal:
fmt.Println("当前任务已停止")
return
case <-time.After(time.Second * 2):
fmt.Println("任务执行中")
}
}
}()
time.Sleep(time.Second * 10)
signal <- struct{}{}
fmt.Println("exit")
}
context
当多个goroutine都需要控制结束,或者这些goriutine所衍生出来的goroutine也需要进行控制的时候就需要用到context,多层级goroutine之间的信号传播需要使用context。
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go gOne(ctx, "任务一")
time.Sleep(time.Second * 10)
cancel()
time.Sleep(time.Second * 2)
fmt.Println("exit")
}
func gOne(ctx context.Context, name string) {
go gTwo(ctx, "任务二")
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 1):
fmt.Println(name + "任务执行中")
}
}
}
func gTwo(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 2):
fmt.Println(name + "任务执行中")
}
}
}