golang通過context控制并發(fā)的應用場景實現(xiàn)
golang 里出現(xiàn)多 goroutine 的場景很常見, 最常用的兩種方式就是 WaitGroup 和 Context, 今天我們了解一下 Context 的應用場景
使用場景
場景一: 多goroutine執(zhí)行超時通知
并發(fā)執(zhí)行的業(yè)務中最常見的就是有協(xié)程執(zhí)行超時, 如果不做超時處理就會出現(xià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() // 防止主進程提前退出 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() 給文本流設置一個時間上限, 結(jié)合 for+select 去接收消息. 當執(zhí)行超時,或手動關(guān)閉都會給 <-ctx.Done() 發(fā)送消息,而且所有使用同一個 context 都會收到這個通知, 免去了一個一個通知的繁瑣代碼
場景二: 類似web服務器中的session
比如在php中(沒用swoole擴展), 一個請求進來, 從 $_REQUEST $_SERVER 能獲取到的是有關(guān)這一條請求的所有信息, 哪怕是使用全局變量也是給這一個請求來服務的, 是線程安全的
但是 golang 就不一樣了, 因為程序本身就能起一個 web sever, 因此就不能隨便使用全局變量了, 不然就是內(nèi)存泄露警告. 但是實際業(yè)務當中需要有一個類似session 的東西來承載單次請求的信息, 舉一個具體的例子就是: 給每次請求加一個 uniqueID 該如何處理? 有了這個 uniqueID, 請求的所有日志都能帶上它, 這樣排查問題的時候方便追蹤一次請求發(fā)生了什么
如下:
func demo2() { pCtx, pCancel := context.WithCancel(context.Background()) pCtx = context.WithValue(pCtx, "parentKey", "parentVale") go watch(pCtx, "[父進程1]") go watch(pCtx, "[父進程2]") cCtx, cCancel := context.WithCancel(pCtx) go watch(cCtx, "[子進程1]") go watch(cCtx, "[子進程2]") fmt.Println(pCtx.Value("parentKey")) fmt.Println(cCtx.Value("parentKey")) time.Sleep(10 * time.Second) fmt.Println("子進程關(guān)閉") cCancel() time.Sleep(5 * time.Second) fmt.Println("父進程關(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() 將影響到當前 context 和子 context, 不會影響到父級.
同時 context.WithValue 也會給 context 帶上自定義的值, 這樣 uniqueID 就能輕松的傳遞了下去, 而不是一層層的傳遞參數(shù), 改func什么的
對于 context 很值得參考的應用有:
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{} }
當使用的時候不需要實現(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
- WithCancel 返回一個帶 cancel 函數(shù)的ctx,
- WithDeadline 在到達指定時間時自動執(zhí)行 cancel()
- WithTimeout 是 WithDeadline的殼子, 區(qū)別就是這個函數(shù)是多少時間過后執(zhí)行 cancel
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
WithValue 繼承父類ctx時順便帶上一個值
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Go用兩個協(xié)程交替打印100以內(nèi)的奇偶數(shù)的方法詳解
這篇文章主要給大家詳細介紹了Go用兩個協(xié)程交替打印100以內(nèi)的奇偶數(shù)的示例代碼,文中給大家介紹了兩個實現(xiàn)方法,使用無緩沖的channel和設置GOMAXPROCS=1,介紹的非常詳細,需要的朋友可以參考下2023-08-08golang并發(fā)執(zhí)行的幾種方式小結(jié)
本文主要介紹了golang并發(fā)執(zhí)行的幾種方式小結(jié),主要包括了Channel,WaitGroup ,Context,使用這三種機制中的一種或者多種可以達到并發(fā)控制很好的效果,具有一定的參考價值,感興趣的可以了解一下2023-08-08golang 實現(xiàn)tcp轉(zhuǎn)發(fā)代理的方法
今天小編就為大家分享一篇golang 實現(xiàn)tcp轉(zhuǎn)發(fā)代理的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08Go?io/fs.FileMode文件系統(tǒng)基本操作和權(quán)限管理深入理解
這篇文章主要為大家介紹了Go?io/fs.FileMode文件系統(tǒng)基本操作和權(quán)限管理深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01golang并發(fā)編程中Goroutine 協(xié)程的實現(xiàn)
Go語言中的協(xié)程是一種輕量級線程,通過在函數(shù)前加go關(guān)鍵字來并發(fā)執(zhí)行,具有動態(tài)棧、快速啟動和低內(nèi)存使用等特點,本文就來詳細的介紹一下,感興趣的可以了解一下2024-10-10