Go語言系列 十六 上下文context

2021-10-07 16:40:35 字數 4404 閱讀 8684

go http包的server中,每乙個請求在都有乙個對應的goroutine去處理。請求處理函式通常會啟動額外的goroutine用來訪問後端服務,比如資料庫和rpc服務。乙個上游服務通常需要訪問多個下游服務,比如終端使用者的身份認證資訊、驗證相關的token、請求的截止時間。當乙個請求被取消或超時時,所有用來處理該請求的 goroutine 都應該迅速退出,然後系統才能釋放這些 goroutine 占用的資源。

傳統方案一:使用sync.waitgroup

問題:只有所有的goroutine都結束了才算結束,只要有乙個goroutine沒有結束, 那麼就會一直等,這顯然對資源的釋放是緩慢的

var wg sync.waitgroup

func

run(task string

)func

main()

// 等待,等待所有的任務都釋放 等待組計數器值為 0

wg.wait()

fmt.

println

("所有任務結束。。。")}

/* -----------------------執行結果----------------------------

task2 start。。。

task1 start。。。

所有任務結束。。。

*/

傳統方案二:使用channel+select

通過在main goroutine中像chan中傳送關閉停止指令,並配合select,從而達到關閉goroutine的目的,這種方式顯然比等待組優雅的多,但是在goroutine中在巢狀goroutine的情況就變得異常複雜。

func

main()

}}()

// 執行10s後停止

time.

sleep

(time.second *10)

fmt.

println

("需要停止任務1。。。"

) stop <-

true

time.

sleep

(time.second *1)

}/*------------------執行結果---------------------------------

任務1 正在執行中...

任務1 正在執行中...

任務1 正在執行中...

任務1 正在執行中...

任務1 正在執行中...

任務1 正在執行中...

需要停止任務1...

任務1 結束了...

*/

func main ()}

}(ctx)

//執行10s後停止

time.

sleep

(time.second*10)

fmt.

println

("需要停止任務1...."

)// 使用context 的cancel 函式停止goroutine

cancel()

// 為了檢測監控過是否停止,如果沒有監控輸出,就表示停止了

time.

sleep

(time.second*4)

}

多個goroutine情況
// 使用context控制多個goroutine

func

watch

(ctx context.context, name string)}

}func

main()

///// 使用channel控制多個goroutine

func

watch

(c chan

bool

, name string)}

}func

main()

上面例子中,啟動了 3 個監控goroutine進行不斷的執行任務,每乙個都使用了context進行跟蹤,當我們使用cancel函式通知取消時,這 3 個goroutine都會被結束canel之後,所有基於這個context或者衍生的子context都會收到通知,這時就可以進行清理操作了,最終釋放 goroutine,這就優雅的解決了 goroutine 啟動後不可控的問題。

type context inte***ce

,如果該方法返回的 chan 可以讀取,則意味著parent context已經發起了取消請求,我們通過 done 方法收到這個訊號後,就應該做清理操作,然後退出 goroutine,釋放資源。

done()

<-

chan

struct

// 返回取消的錯誤原因,因為什麼 context 被取消。

err(

)error

// 獲取該 context 上繫結的值,是乙個鍵值對,所以要通過乙個 key 才可以獲取對應的值,這個值一般是執行緒安全的。

value

(key inte***ce

)inte***ce

}

func  stream (ctx context.context, out chan

<- value)

error

select

}}

go幫我們實現了2個context介面,我們**中最開始都是以這兩個內建的作為最頂層的partent context,衍生出更多的子context

var

( background =

new(emptyctx)

todo =

new(emptyctx)

)func

background

() context

func

todo

() context

/type emptyctx int

func

(*emptyctx)

deadline()

(deadline time.time, ok bool

)func

(*emptyctx)

done()

<-

chan

struct

func

(*emptyctx)

err(

)error

func

(*emptyctx)

value

(key inte***ce

)inte***ce

context包為我們提供的with 系列的函式,可以讓我們在原來的context上衍生出子context

//返回子 context,以及乙個取消函式用來取消 context。

func

withcancel

(parent context)

(ctx context, cancel cancelfunc)

//傳入截止時間引數,意味著到了這個時間點,會自動取消 context,也可以不等到這個時候,可以提前通過取消函式進行取消。

func

withdeadline

(parent context, deadline time.time)

(context, cancelfunc)

//傳入乙個時間引數,多少時間之後取消context,當然我們也可以不等到這個時候,可以提前通過取消函式進行取消。

func

withtimeout

(parent context, timeout time.duration)

(context, cancelfunc)

//生成乙個繫結了乙個鍵值對資料的 context,即給context設定值,這個繫結的資料可以通過 context.value 方法訪問到.

func

withvalue

(parent context, key, val inte***ce

) context

context最佳實戰 參考

Js系列三 執行上下文

js 在執行的時候會進入乙個特定的環境中,這個環境被稱為執行上下文。在js中執行環境主要包括以下三種情況 1 全域性環境既js 執行時首先進入的環境。2 函式環境 函式執行時會進入當前函式的環境執行 由此我們知道在js程式執行過程中必然會出現多個執行環境 執行上下文 js引擎以函式呼叫棧的方式來處理...

Go語言中的上下文取消操作詳解

前言 許多使用go的人,都會用到它的上下文庫。大多數使用 context 進行下游操作,比如發出http呼叫,或者從資料庫獲取資料,或者在協程中執行非同步操作。最常見的用法是傳遞可由所有下游操作使用的公共資料。然而,乙個不太為人所知,但非常有用的上下文特性是,它能夠在中途取消或停止乙個操作。本篇文章...

JS學習系列 05 執行上下文

在我們前面理解了作用域之後,作用域鏈 這個概念就產生了。那麼作用域鏈是什麼意思,它又是怎麼形成的,跟哪些概念有關係,這就是我接下來幾章想和大家 的內容 執行上下文 變數物件和作用域鏈。根據順序我們也可以看出來,想要理解作用域鏈,執行上下文是我們碰到的第乙個坎。這一章我們就來討論一下到底什麼是執行上下...