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啓動後不可控的問題