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