1、WaitGroup
它是一種控制並發的方式,它的這種方式是控制多個goroutine同時完成。
func main() {
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("好了,大家都干完了,放工")
}
一個很簡單的例子,一定要例子中的2個goroutine同時做完,才算是完成,先做好的就要等着其他未完成的,所有的goroutine要都全部完成才可以。
2、chan通知
我們都知道一個goroutine啟動后,我們是無法控制他的,大部分情況是等待它自己結束,那么如果這個goroutine是一個不會自己結束的后台goroutine呢?比如監控等,會一直運行的。
這種情況化,一直傻瓜式的辦法是全局變量,其他地方通過修改這個變量完成結束通知,然后后台goroutine不停的檢查這個變量,如果發現被通知關閉了,就自我結束。
這種方式也可以,但是首先我們要保證這個變量在多線程下的安全,基於此,有一種更好的方式:chan + select 。
func main() {
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)
}
3、WithTimeout 超時自動取消方法
當執行一個go 協程時,超時自動取消協程
// 模擬一個最小執行時間的阻塞函數
func inc(a int) int {
res := a + 1 // 雖然我只做了一次簡單的 +1 的運算,
time.Sleep(1 * time.Second) // 但是由於我的機器指令集中沒有這條指令,
// 所以在我執行了 1000000000 條機器指令, 續了 1s 之后, 我才終於得到結果。B)
return res
}
// 向外部提供的阻塞接口
// 計算 a + b, 注意 a, b 均不能為負
// 如果計算被中斷, 則返回 -1
func Add(ctx context.Context, a, b int) int {
res := 0
for i := 0; i < a; i++ {
res = inc(res)
select {
case <-ctx.Done():
return -1
default:
}
}
for i := 0; i < b; i++ {
res = inc(res)
select {
case <-ctx.Done():
return -1
default:
}
}
return res
}
計算 a+b
func main() {
// 使用開放的 API 計算 a+b
a := 1
b := 2
timeout := 2 * time.Second
ctx, _ := context.WithTimeout(context.Background(), timeout)
res := Add(ctx, 1, 2)
fmt.Printf("Compute: %d+%d, result: %d\n", a, b, res)
}
輸出結果:Compute: 1+2, result: -1
4、WithCancel 手動取消方法
func main() {
// 手動取消
a := 1
b := 2
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(2 * time.Second)
cancel() // 在調用處主動取消
}()
res := Add(ctx, 1, 2)
fmt.Printf("Compute: %d+%d, result: %d\n", a, b, res)
}
輸出結果:Compute: 1+2, result: -1
