您好,登錄后才能下訂單哦!
context在golang中的作用是什么?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
當一個goroutine可以啟動其他goroutine,而這些goroutine可以啟動其他goroutine,依此類推,則第一個goroutine應該能夠向所有其它goroutine發送取消信號。
上下文包的唯一目的是在goroutine之間執行取消信號,而不管它們如何生成。上下文的接口定義為:
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <- chan struct{} Err() error Value(key interface{}) interface{} }
Deadline:第一個值是截止日期,此時上下文將自動觸發“取消”操作。第二個值是布爾值,true表示設置了截止日期,false表示未設置截止時間。如果沒有設置截止日期,則必須手動調用cancel函數來取消上下文。
Done:返回一個只讀通道(僅在取消后),鍵入struct {},當該通道可讀時,表示父上下文已經發起了取消請求,根據此信號,開發人員可以執行一些清除操作,退出goroutine
Err:返回取消上下文的原因
Value:返回綁定到上下文的值,它是一個鍵值對,因此您需要傳遞一個Key來獲取相應的值,此值是線程安全的
要創建上下文,必須指定父上下文。兩個內置上下文(背景和待辦事項)用作頂級父上下文:
var ( background = new(emptyCtx) todo = new(emptyCtx) ) func Background() Context { return background } func TODO() Context { return todo }
背景,主要ü在主函數,初始化和測試代碼的sed,是樹結構中,根上下文,這是不能被取消的頂層語境。TODO,當您不知道要使用什么上下文時,可以使用它。它們本質上都是emptyCtx類型,都是不可取消的,沒有固定的期限,也沒有為Context賦任何值:鍵入emptyCtx int
type emptyCtx int func (_ *emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (_ *emptyCtx) Done() <- chan struct{} { return nil } func (_ *emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil }
上下文包還具有幾個常用功能:func WithCancel(父上下文)(ctx上下文,取消CancelFunc)func WithDeadline(父上下文,截止時間.Time)(上下文,CancelFunc)func WithTimeout(父上下文,超時時間。持續時間)(上下文,CancelFunc)func WithValue(父上下文,鍵,val接口{})上下文
請注意,這些方法意味著可以一次繼承上下文以實現其他功能,例如,使用WithCancel函數傳入根上下文,它會創建一個子上下文,該子上下文具有取消上下文的附加功能,然后使用此方法將context(context01)作為父上下文,并將其作為第一個參數傳遞給WithDeadline函數,與子context(context01)相比,獲得子context(context02),它具有一個附加功能,可在之后自動取消上下文最后期限。
對于通道,盡管通道也可以通知許多嵌套的goroutine退出,但通道不是線程安全的,而上下文是線程安全的。
例如:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ch chan bool, index int) { for { select { case v := <- ch: fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor2: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func monitor1(ch chan bool, index int) { for { go monitor2(ch, index) select { case v := <- ch: // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor1: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func main() { var stopSingal chan bool = make(chan bool, 0) for i := 1; i <= 5; i = i + 1 { go monitor1(stopSingal, i) } time.Sleep(1 * time.Second) // close all gourtines cancel() // waiting 10 seconds, if the screen does not display <monitorX: xxxx in progress...>, all goroutines have been shut down time.Sleep(10 * time.Second) println(runtime.NumGoroutine()) println("main program exit!!!!") }
執行的結果是:
monitor1: 5 in progress...
monitor2: 5 in progress...
monitor1: 2 in progress...
monitor2: 2 in progress...
monitor2: 1 in progress...
monitor1: 1 in progress...
monitor1: 4 in progress...
monitor1: 3 in progress...
monitor2: 4 in progress...
monitor2: 3 in progress...
monitor1: 4, the received channel value is: false, ending
monitor1: 3, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 1, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 5, the received channel value is: false, ending
monitor1: 2, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
1
main program exit!!!!
這里使用一個通道向所有goroutine發送結束通知,但是這里的情況相對簡單,如果在一個復雜的項目中,假設多個goroutine有某種錯誤并重復執行,則可以重復關閉或關閉該通道通道,然后向其寫入值,從而觸發運行時恐慌。這就是為什么我們使用上下文來避免這些問題的原因,以WithCancel為例:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ctx context.Context, number int) { for { select { case v := <- ctx.Done(): fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number,v) return default: fmt.Printf("monitor: %v in progress...\n", number) time.Sleep(2 * time.Second) } } } func monitor1(ctx context.Context, number int) { for { go monitor2(ctx, number) select { case v := <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number, v) return default: fmt.Printf("monitor: %v in progress...\n", number) time.Sleep(2 * time.Second) } } } func main() { var ctx context.Context = nil var cancel context.CancelFunc = nil ctx, cancel = context.WithCancel(context.Background()) for i := 1; i <= 5; i = i + 1 { go monitor1(ctx, i) } time.Sleep(1 * time.Second) // close all gourtines cancel() // waiting 10 seconds, if the screen does not display <monitor: xxxx in progress>, all goroutines have been shut down time.Sleep(10 * time.Second) println(runtime.NumGoroutine()) println("main program exit!!!!") }
WithTimeout和WithDeadline在用法和功能上基本相同,它們都表示上下文將在一定時間后自動取消,唯一的區別可以從函數的定義中看出,傳遞給WithDeadline的第二個參數是類型time.Duration類型,它是一個相對時間,表示取消超時后的時間。例:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ctx context.Context, index int) { for { select { case v := <- ctx.Done(): fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor2: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func monitor1(ctx context.Context, index int) { for { go monitor2(ctx, index) select { case v := <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor1: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func main() { var ctx01 context.Context = nil var ctx02 context.Context = nil var cancel context.CancelFunc = nil ctx01, cancel = context.WithCancel(context.Background()) ctx02, cancel = context.WithDeadline(ctx01, time.Now().Add(1 * time.Second)) // If it's WithTimeout, just change this line to "ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)" defer cancel() for i := 1; i <= 5; i = i + 1 { go monitor1(ctx02, i) } time.Sleep(5 * time.Second) if ctx02.Err() != nil { fmt.Println("the cause of cancel is: ", ctx02.Err()) } println(runtime.NumGoroutine()) println("main program exit!!!!") }
一些必需的元數據也可以通過上下文傳遞,該上下文將附加到上下文中以供使用。元數據作為鍵值傳遞,但請注意,鍵必須具有可比性,并且值必須是線程安全的。
package main import ( "runtime" "fmt" "time" "context" ) func monitor(ctx context.Context, index int) { for { select { case <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor %v, end of monitoring. \n", index) return default: var value interface{} = ctx.Value("Nets") fmt.Printf("monitor %v, is monitoring %v\n", index, value) time.Sleep(2 * time.Second) } } } func main() { var ctx01 context.Context = nil var ctx02 context.Context = nil var cancel context.CancelFunc = nil ctx01, cancel = context.WithCancel(context.Background()) ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second) var ctx03 context.Context = context.WithValue(ctx02, "Nets", "Champion") // key: "Nets", value: "Champion" defer cancel() for i := 1; i <= 5; i = i + 1 { go monitor(ctx03, i) } time.Sleep(5 * time.Second) if ctx02.Err() != nil { fmt.Println("the cause of cancel is: ", ctx02.Err()) } println(runtime.NumGoroutine()) println("main program exit!!!!") }
關于上下文,還有一些注意事項:不要將Context存儲在結構類型中,而是將Context明確傳遞給需要它的每個函數,并且Context應該是第一個參數。
即使函數允許,也不要傳遞nil Context,或者如果您不確定要使用哪個Context,請傳遞context。不要將可能作為函數參數傳遞給上下文值的變量傳遞。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。