僅執行單次的任務
全域資源的加載(初始化), 多任務中公共資源銷毀
// 初始化作業// 1. 通過init 方法,僅執行一次,但是不能重復呼叫// 模擬全域配置type AppConfig struct { Name string IP string Version string} var appconfig *AppConfigfunc init() { appconfig=new(AppConfig) appconfig.Name = "myapp" appconfig.IP = "192.168.66.88" appconfig.Version="v1.0"}// 2. 使用sync.once方法,保證我們代碼執行一次// 銷毀全域資源var once sync.Oncefunc DelAPP() { once.Do(func(){ appconfig = nil log.Println("delete app!") })}撰寫異步方法
假如任務耗時比較長可以考慮把方法撰寫成異步的(類似前端知識中的ajax請求),例如任務中有需要下載的任務,這時候下載任務可以考慮撰寫成異步,例如資料存盤任務等
撰寫異步任務方法要點: 任務內部啟動goroutine處理任務,并立即回傳>
// 撰寫一個資料庫異步處理任務(模擬)func StorageData(data string){ // 預處理資料 log.Println("入庫前預處理: ",data) go func(data){ time.Sleep(time.Sencond*3) log.Println("資料庫存盤完成") }}撰寫異步帶回呼方法
異步方法的基礎上,如果想處理異步的結果,需要傳遞一個回呼方法
注意: 撰寫異步的基礎上,加上回呼方法(處理結果)
// 撰寫一個異步爬取web的方法package mainimport ( "io/ioutil" "log" "net/http" "os" "time")func CrawlUrl(url string,fn func(response *http.Response)) { go func() { response,err:=http.Get(url) if err!=nil{ log.Println(err) return } fn(response) }()}func main() { CrawlUrl("http://www.baidu.com", func(response *http.Response) { data,err:=ioutil.ReadAll(response.Body) if err!=nil{ return } log.Println("Content length: ",len(data)) }) log.Println("main func do other thing") CrawlUrl("http://i2.hdslb.com/bfs/archive/bc8adff1dafe7494c6b2155ec82725af0034c31b.png", func(response *http.Response) { data,err:=ioutil.ReadAll(response.Body) if err!=nil{ log.Println(err) return } f,_:=os.Create("1.png") defer f.Close() _, _ = f.Write(data) }) time.Sleep(time.Second*5)}
等待所有任務結束
啟動多個子任務,等待子任務結束
不需要結果
需要結果的
// 1. 需要回傳結果,僅需要完成任務即可func CallTaskNoRsult(){ wg := sync.WaitGroup{} wg.Add(5) for i:=0;i<5;i++{ go func(num int) { defer wg.Done() log.Println("任務",num,"完成") }(i) } wg.Wait()}// 2. 需要完成任務還需要回傳結果,可能需要做后續處理func CallTaskResults() { // 創建一個channel 用于接收任務處理結果 results:=make(chan string,10) defer close(results) // 隨機種子 rand.Seed(time.Now().UnixNano()) for i:=0;i<10;i++{ go func(num int) { // 隨機生成一個結果,并把結果添加到結果佇列 results<-fmt.Sprintf("Task %d# result: %d",num,rand.Intn(20)+10) }(i) } //等待輸出結果 for i:=0;i<10;i++{ log.Println(<-results) }}
等待任意一個任務完成
執行等待多個子任務,只要任意一個任務完成即可,例如下載檔案,假如有多個下載源,不確定哪個源網路情況好,可以考慮使用這個方式
// 模擬多源下載,任意一個任務完成就結束func Download(){ // 定義一個channel,用于接收結果 result:=make(chan string) defer close(result) // 種子 rand.Seed(time.Now().UnixNano()) // 異步獲取子任務下載 資料 for i:=0;i<10;i++{ go func(num int) { // 隨機休眠一段時間 time.Sleep(time.Second*time.Duration(rand.Intn(5)+1)) result<-fmt.Sprintf("Task %d# download ok!",num) }(i) } // 等待子任務完成 log.Println(<-result)}
協作等待其他任務結果
稍微復雜的一些業務,其中的一個子任務需要另一個子任務的結果

// 異步協作,任務B需要任務的計算結果func BWaitA() { // 創建channel 用于任務通訊 ch:=make(chan string,1) // 快取為1 defer close(ch) // 創建wait group wg:= sync.WaitGroup{} wg.Add(2) // 啟動任務A go func() { defer wg.Done() log.Println("A do working...") time.Sleep(time.Second*3) log.Println("A end calc...., send result to B") ch<-"data for B" log.Println("A do send result to B, do other thing!") }() // 啟動任務B go func() { defer wg.Done() log.Println("B do working...") time.Sleep(time.Second*2) log.Println("B wait A...") log.Println(<-ch) log.Println("B user A result do other thing!") }() wg.Wait()}任務取消
某些特殊的情景下,我們可能需要取消子任務的執行,例如主任務因為用戶的原因,需要提前結束,通知所有的子任務結束
單層任務取消
func CancleTask() { // 創建一個cancele 的channel 用于通知子任務結束 canncle:=make(chan struct{}) // 封裝一個方法用于檢查 任務是否被取消了 iscancle:= func() bool { select { case <-canncle: return true default: return false } } //模擬啟動子任務 for i:=0;i<5;i++{ go func(num int) { for{ // 監聽任務是否被取消 if iscancle(){ // 如果被取消則 退出任務 log.Printf("Task #%d is canceled!\n",num) return } log.Printf("Task #%d is working...\n",num) time.Sleep(time.Millisecond *50) } }(i) } // 模擬任務運行一段時間 log.Println("main working for an while...") time.Sleep(time.Millisecond *100) // 取消任務,利用關閉channel的廣播特性 close(canncle) time.Sleep(time.Second)}
多層任務取消
當任務比較復雜的時候,更多情況可能是關聯任務,例如下圖這樣的多層任務
Context 初識
根context節點,context.BackGroud() 創建一個空的context(根節點),沒有任何作用,為了給子contentx繼承的
ctx,canncel:=context.WithCancel(ctx),回傳一個ctx子節點,回傳一個cancleFuc
呼叫取消方法,給所有的子節點發送取消信號
子任務中監聽取消信號并退出任務
使用context取消任務
func CancelWithCtx() { // 創建空ctx,根節點 root:=context.Background() // 定義isCancel方法 isCanncel:= func(ctx context.Context) bool { select { case <-ctx.Done(): return true default: return false } } Task:=func (ctx context.Context, num int){ // 啟動子任務 go func() { for{ if isCanncel(ctx){ // 如果被取消則 退出任務 log.Printf("Task #%d sub is canceled!\n",num) return } } }() for{ if isCanncel(ctx){ // 如果被取消則 退出任務 log.Printf("Task #%d is canceled!\n",num) return } log.Printf("Task #%d is working...\n",num) time.Sleep(time.Millisecond*200) } } // 創建子ctx,用于取消 ctxOne,cancelOne:=context.WithCancel(root) go Task(ctxOne,1) // 創建子ctx,用于取消 ctxTwo,_:=context.WithCancel(root) go Task(ctxTwo,2) // 模擬保證所有的goroutine都運行起來 time.Sleep(time.Second) // 取消 第一個任務 cancelOne() time.Sleep(time.Second)}output:
2019/06/27 11:55:45 Task #1 is working...2019/06/27 11:55:45 Task #2 is working...2019/06/27 11:55:46 Task #1 is working...2019/06/27 11:55:46 Task #2 is working...2019/06/27 11:55:46 Task #1 is working...2019/06/27 11:55:46 Task #2 is working...2019/06/27 11:55:46 Task #1 is working...2019/06/27 11:55:46 Task #2 is working...2019/06/27 11:55:46 Task #1 is working...2019/06/27 11:55:46 Task #2 is working...2019/06/27 11:55:46 Task #1 sub is canceled!2019/06/27 11:55:46 Task #1 is canceled!2019/06/27 11:55:46 Task #2 is working...2019/06/27 11:55:47 Task #2 is working...2019/06/27 11:55:47 Task #2 is working...2019/06/27 11:55:47 Task #2 is working...2019/06/27 11:55:47 Task #2 is working...
context在net/http包中的演示
package mainimport ( "fmt" "net/http" "time")func CtxTest(w http.ResponseWriter, r *http.Request){ // 獲取 ctx ctx := r.Context() fmt.Println("processing request") select { case <-time.After(5 * time.Second): // 模擬請求處理完 w.Write([]byte("request processed")) case <-ctx.Done(): // 網頁加載完畢 Done 信號發出 // 用戶取消的時候,獲取取消信號s fmt.Println( "request cancelled") }}func main() { // 系結路由處理方法 http.HandleFunc("/",CtxTest) // 啟動服務 http.ListenAndServe(":8000", nil)}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/59803.html
標籤:Go
