1. WaitGroup
package main
import (
"fmt"
"sync"
"time"
)
func main() {
// 适用于同一个大任务拆分为多个小任务共同执行,最后等待所有任务同时完成结束
var wg sync.WaitGroup
// 需要执行两个任务
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println("this is goroutine1")
time.Sleep(1 * time.Second)
}()
go func() {
defer wg.Done()
fmt.Println("this is goroutine2")
time.Sleep(3 * time.Second)
}()
// 等待所有任务执行完毕
wg.Wait()
fmt.Println("all goroutine are done")
}
2. Channel
package main
import (
"fmt"
"time"
)
func main() {
// 定义一个channel,等待一个任务执行,待达到某个条件时,向channel发送通知信号,通过channel+select,优雅的通知goroutine结束
// 局限性,不适用于控制多个goroutine,或者一个goroutine中又运行了多个goroutine
sig := make(chan bool)
go func() {
for {
select {
case <- sig:
fmt.Println("任务退出")
return
default:
fmt.Println("任务正在执行中....")
time.Sleep(2 * time.Second)
}
}
}()
time.Sleep(5 * time.Second)
fmt.Println("通知任务结束执行")
sig <- true
// 防止main goroutine提前退出
time.Sleep(5 * time.Second)
}
3. Context
通过控制Context上下文能够达到多层级的goroutine同一控制的效果
package main
import (
"context"
"fmt"
"time"
)
func A(ctx context.Context, name string) {
go B(ctx, name)
for {
select {
case <- ctx.Done():
fmt.Println(name, "A退出执行")
return
default:
fmt.Println(name, "A正在执行...")
time.Sleep(2 * time.Second)
}
}
}
func B(ctx context.Context, name string) {
for {
select {
case <- ctx.Done():
fmt.Println(name, "B退出执行")
return
default:
fmt.Println(name, "B正在执行...")
time.Sleep(2 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
// 模拟一个client请求
go A(ctx, "[我乃请求1]")
time.Sleep(3 * time.Second)
fmt.Println("client断开连接,通知对应请求的A,B退出")
cancel() // 假设满足某条件client断开连接,那么就向ctx中发送取消信号
time.Sleep(3 * time.Second)
}
模拟一个client连接请求,开启一个goroutine A,同时开启一个goroutine B进行处理,两个goroutine都使用context进行跟踪,当使用cancle()函数进行退出时,两个goroutine都会被结束.
使用context的控制能力,像一个控制器一样,当发送退出信号时,所有基于这个context或者子context都会收到通知进行退出操作,最终释放goroutine,解决goroutine启动后不可控的问题