golang 里出現(xiàn)多 goroutine 的場景很常見, 最常用的兩種方式就是 WaitGroup 和 Context, 今天我們了解一下 Context 的應(yīng)用場景
目前創(chuàng)新互聯(lián)公司已為1000多家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站托管運營、企業(yè)網(wǎng)站設(shè)計、大城網(wǎng)站維護(hù)等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
使用場景
場景一: 多goroutine執(zhí)行超時通知
并發(fā)執(zhí)行的業(yè)務(wù)中最常見的就是有協(xié)程執(zhí)行超時, 如果不做超時處理就會出現(xiàn)一個僵尸進(jìn)程, 這累計的多了就會有一陣手忙腳亂了, 所以我們要在源頭上就避免它們
看下面這個示例:
package main import ( "context" "fmt" "time" ) /** 同一個content可以控制多個goroutine, 確保線程可控, 而不是每新建一個goroutine就要有一個chan去通知他關(guān)閉 有了他代碼更加簡潔 */ func main() { fmt.Println("run demo \n\n\n") demo() } func demo() { ctx, cancel := context.WithTimeout(context.Background(), 9*time.Second) go watch(ctx, "[線程1]") go watch(ctx, "[線程2]") go watch(ctx, "[線程3]") index := 0 for { index++ fmt.Printf("%d 秒過去了 \n", index) time.Sleep(1 * time.Second) if index > 10 { break } } fmt.Println("通知停止監(jiān)控") // 其實此時已經(jīng)超時, 協(xié)程已經(jīng)提前退出 cancel() // 防止主進(jìn)程提前退出 time.Sleep(3 * time.Second) fmt.Println("done") } func watch(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Printf("%s 監(jiān)控退出, 停止了...\n", name) return default: fmt.Printf("%s goroutine監(jiān)控中... \n", name) time.Sleep(2 * time.Second) } } }
使用 context.WithTimeout() 給文本流設(shè)置一個時間上限, 結(jié)合 for+select 去接收消息. 當(dāng)執(zhí)行超時,或手動關(guān)閉都會給 <-ctx.Done() 發(fā)送消息,而且所有使用同一個 context 都會收到這個通知, 免去了一個一個通知的繁瑣代碼
場景二: 類似web服務(wù)器中的session
比如在php中(沒用swoole擴展), 一個請求進(jìn)來, 從 $_REQUEST $_SERVER 能獲取到的是有關(guān)這一條請求的所有信息, 哪怕是使用全局變量也是給這一個請求來服務(wù)的, 是線程安全的
但是 golang 就不一樣了, 因為程序本身就能起一個 web sever, 因此就不能隨便使用全局變量了, 不然就是內(nèi)存泄露警告. 但是實際業(yè)務(wù)當(dāng)中需要有一個類似session 的東西來承載單次請求的信息, 舉一個具體的例子就是: 給每次請求加一個 uniqueID 該如何處理? 有了這個 uniqueID, 請求的所有日志都能帶上它, 這樣排查問題的時候方便追蹤一次請求發(fā)生了什么
如下:
func demo2() { pCtx, pCancel := context.WithCancel(context.Background()) pCtx = context.WithValue(pCtx, "parentKey", "parentVale") go watch(pCtx, "[父進(jìn)程1]") go watch(pCtx, "[父進(jìn)程2]") cCtx, cCancel := context.WithCancel(pCtx) go watch(cCtx, "[子進(jìn)程1]") go watch(cCtx, "[子進(jìn)程2]") fmt.Println(pCtx.Value("parentKey")) fmt.Println(cCtx.Value("parentKey")) time.Sleep(10 * time.Second) fmt.Println("子進(jìn)程關(guān)閉") cCancel() time.Sleep(5 * time.Second) fmt.Println("父進(jìn)程關(guān)閉") pCancel() time.Sleep(3 * time.Second) fmt.Println("done") }
最開始的 context.WithCancel(context.Background()) 中 context.Background() 就是一個新建的 context, 利用 context 能繼承的特性, 可以將自己的程序構(gòu)建出一個 context 樹, context 執(zhí)行 cancel() 將影響到當(dāng)前 context 和子 context, 不會影響到父級.
同時 context.WithValue 也會給 context 帶上自定義的值, 這樣 uniqueID 就能輕松的傳遞了下去, 而不是一層層的傳遞參數(shù), 改func什么的
對于 context 很值得參考的應(yīng)用有:
Context 相關(guān) func 和接口
繼承 context 需要實現(xiàn)如下四個接口
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
當(dāng)使用的時候不需要實現(xiàn)接口, 因為官方包里已經(jīng)基于 emptyCtx 實現(xiàn)了一個, 調(diào)用方法有
var ( background = new(emptyCtx) todo = new(emptyCtx) ) // 這個是最初始的ctx, 之后的子ctx都是繼承自它 func Background() Context { return background } // 不清楚context要干嘛, 但是就得有一個ctx的用這個 func TODO() Context { return todo }
繼承用的函數(shù)
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
WithValue 繼承父類ctx時順便帶上一個值
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
網(wǎng)站標(biāo)題:golang通過context控制并發(fā)的應(yīng)用場景實現(xiàn)
文章分享:http://www.chinadenli.net/article34/joejpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、移動網(wǎng)站建設(shè)、小程序開發(fā)、網(wǎng)站收錄、Google、域名注冊
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)