点击上方蓝色“Go语言中文网”关注我们,领全套Go资料,每天学习 Go 语言
ctx context.Context
使用 WaitGroup
学 Go 时肯定要学习如何使用并发(goroutine),而开发者该如何控制并发呢?其实有两种方式,一种是WaitGroup,另一种就是 context,而什么时候需要用到 WaitGroup 呢?很简单,就是当您需要将同一件事情拆成不同的 Job 进行执行,最后需要等到全部的 Job 都执行完成才继续执行主程序,这时候就需要用到 WaitGroup,看个实际例子
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
time.Sleep(2 * time.Second)
fmt.Println("job 1 done.")
wg.Done()
}()
go func() {
time.Sleep(1 * time.Second)
fmt.Println("job 2 done.")
wg.Done()
}()
wg.Wait()
fmt.Println("All Done.")
}
wg.Wait()
使用 channel + select
package main
import (
"fmt"
"time"
)
func main() {
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
fmt.Println("got the stop channel")
return
default:
fmt.Println("still working")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(5 * time.Second)
fmt.Println("stop the gorutine")
stop <- true
time.Sleep(5 * time.Second)
}
上面可以看到,透过 select + channel 可以快速解决这个问题,只要在任何地方将 bool 值丢入 stop channel 就可以停止背景正在处理的 Job。上述用 channel 来解决此问题,但是现在有个问题,假设背景有跑了无数个 goroutine,或者是 goroutine 内又有跑 goroutine 呢,变得相当复杂,例如底下的状况
这边就没办法用 channel 方式来进行处理了,而需要用到今天的重点 context。
认识 context
context.Background()
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("got the stop channel")
return
default:
fmt.Println("still working")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(5 * time.Second)
fmt.Println("stop the gorutine")
cancel()
time.Sleep(5 * time.Second)
}
context.WithCancel
ctx, cancel := context.WithCancel(context.Background())
cancel func
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, "node01")
go worker(ctx, "node02")
go worker(ctx, "node03")
time.Sleep(5 * time.Second)
fmt.Println("stop the gorutine")
cancel()
time.Sleep(5 * time.Second)
}
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "got the stop channel")
return
default:
fmt.Println(name, "still working")
time.Sleep(1 * time.Second)
}
}
}
上面透过一个 context 可以一次停止多个 worker,看逻辑如何宣告 context 以及什么时机去执行 cancel(),通常我个人都是搭配 graceful shutdown 进行取消运转的工作,或者是停止资料库连线等等。。
心得
初学 Go 时,如果还不常使用 goroutine,其实也不会理解到 context 的使用方式及时机,等到需要有背景处理,以及该如何停止 Job,这时候才渐进了解到使用方式,当然 context 不只有这个使用方式,未来将会介绍其他使用方式。
原文链接:https://blog.wu-boy.com/2020/05/understant-golang-context-in-10-minutes/
推荐阅读
喜欢本文的朋友,欢迎关注“Go语言中文网”: