3.1 go context代码示例
context.WithCancel
返回两个有关联的对象,ctx与cancel,调用cancel发送一个空struct给ctx,ctx一旦接收到该对象后,就终止goroutine的执行;
ctx是线程安全的,可以同时传递给多个goroutine,触发cancel时,取消所有goroutine的执行
package main import ( "context" "fmt" "time" ) func testContext(){ ctx,cancel := context.WithCancel(context.Background()) go d1(ctx) go d2(ctx) time.Sleep(7*time.Second) cancel() } func d1(ctx context.Context){ i:=0 for { time.Sleep(1*time.Second) i++ select { case <- ctx.Done(): fmt.Println("d1 over") return default: fmt.Println("d1 ",i) } } } func d2(ctx context.Context){ fmt.Println("d2 start") <- ctx.Done() fmt.Println("d2 over") } func main(){ testContext() fmt.Println("main over") }
输出
d2 start d1 1 d1 2 d1 3 d1 4 d1 5 d1 6 main over
context适用于这样的情况,你需要持续处理请求,有多个case分支,case分支中持续处理着数据,
select 之前的代码不会被终止,下面的这种写法,会终止return之后的代码、以及其他case分支的代码
case <- ctx.Done():
fmt.Println("d1 over")
return
使用场景举例
package main import ( "context" "fmt" "time" ) func d1(i *int) chan int{ var cc = make(chan int) go func() { for { time.Sleep(1*time.Second) *i = *i + 1 cc <- *i } }() return cc } func textContext(cc chan int,ctx context.Context) { for { select { case <- ctx.Done(): fmt.Println("context done ") return case n:= <- cc : fmt.Println(n) } } fmt.Println("wg done") } func main() { ctx,cancel := context.WithCancel(context.Background()) i:= 0 var cc = d1(&i) go textContext(cc,ctx) for { time.Sleep(1*time.Second) if i > 10 { cancel() break } } }
context.WithTimeout
package main import ( "fmt" "context" "time" ) func d1(ctx context.Context){ i := 0 for{ time.Sleep(1*time.Second) select{ case <- ctx.Done(): fmt.Println("d1 over") return default: fmt.Println("d1:",i) } } } func test(){ ctx,cancel := context.WithTimeout(context.Background(),5*time.Second) go d1(ctx) fmt.Println("begin sleep 10 sec") time.Sleep(10*time.Second) fmt.Println("10 sec over") cancel() } func main(){ test() }
[root@phoenix go]# go run cc.go begin sleep 10 sec d1: 0 d1: 0 d1: 0 d1: 0 d1 over 10 sec over
无法中止正在执行中case分支
package main import ( "context" "fmt" "time" ) func main() { ctx,cancel:= context.WithTimeout(context.Background(),5*time.Second) go timeOut(ctx) time.Sleep(10*time.Second) fmt.Println("强制唤醒") cancel() time.Sleep(5*time.Second) fmt.Println("主程序结束") } func timeOut(ctx context.Context) { for{ select { case <- ctx.Done(): fmt.Println("context done") default: fmt.Println("沉睡100秒") time.Sleep(100*time.Second) } } }
沉睡100秒这个case分支一旦开始执行,除非main协程结束,否则context是无法中止其执行的;5秒超时context发送了结束信号,但select有case未执行完,其channel处于阻塞状态,所以无法中止程序
沉睡100秒
强制唤醒
主程序结束
golang context包 WithValue
func process(ctx context.Context) { ret,ok := ctx.Value("trace_id").(int) if !ok { ret = 21342423 } fmt.Printf("ret:%d\n", ret) s , _ := ctx.Value("session").(string) fmt.Printf("session:%s\n", s) } func main() { ctx := context.WithValue(context.Background(), "trace_id", 2222222) ctx = context.WithValue(ctx, "session", "sdlkfjkaslfsalfsafjalskfj") process(ctx) }