一个请求,可能涉及多个API调用,多个goroutine,如何在多个API 之间,以及多个goroutine之间协做和传递信息,就是一个问题。golang
好比一个网络请求Request,须要开启一些goroutine去访问后端资源(好比,数据库,RPC服务等),这些goroutine又可能会开启其余的goroutine,如何跟踪和控制这些goroutine呢?数据库
golang定义了 context包,用于解决这个问题。后端
context
能够在多个API和进程之间,实现deadlines
截止日期操做、cancelation signals
取消操做以及传递request-scoped
的值(即传递参数)。网络
context
一个最大的特色是能够继承。父context,能够派生子context,子context有又能够派生子context。若是父context被取消,子context及其子context都会被取消。并发
经常使用派生的方法有:WithCancel
WithDeadline
WithTimeout
WithValue
.
其中,函数
WithCancel
用于主动取消自身以及子context。WithDeadline
和WithTimeout
用于超时状况下的取消自身以及子context。WithTimeout
实现上直接调用WithDeadline
。WithValue
用于context
传递request-scoped
的参数,而不是向函数传递的可选参数。下面例子演示如何控制多个goroutine的退出。post
首先,启动3个goroutine,分别命名为"1"、"2"、"3",每一个goroutine又启动一个goroutine。也就是有6个goroutine。它们之间的关系以下:.net
最后,在main
中调用取消操做cancel
,先是1,2,3收到信号退出,1,2,3退出的时候又调用它们的cancel
,这样全部的goroutine都被取消并退出。code
具体代码以下:blog
package main import ( "context" "fmt" "time" ) func PrintTask(ctx context.Context, taskName string) { for { select { case <- ctx.Done(): fmt.Println("task:", taskName, " exit...") return default: time.Sleep(1*time.Second) fmt.Println("task:", taskName, " doing something...") } } } func mainTask(ctx context.Context, taskName string) { ctx1, cancel := context.WithCancel(ctx) defer cancel() // create a new task newTaskName := taskName + "1" go PrintTask(ctx1, newTaskName) for { select { case <- ctx.Done(): fmt.Println("task:", taskName, " exit...") return default: time.Sleep(1*time.Second) fmt.Println("task:", taskName, " doing something...") } } } func main() { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) go mainTask(ctx, "1") go mainTask(ctx, "2") go mainTask(ctx, "3") time.Sleep(3*time.Second) cancel() fmt.Println("main exit...") time.Sleep(3*time.Second) }
输出
task: 1 doing something... task: 21 doing something... task: 11 doing something... task: 3 doing something... task: 2 doing something... task: 31 doing something... task: 3 doing something... task: 2 doing something... task: 11 doing something... task: 21 doing something... task: 31 doing something... task: 1 doing something... task: 11 doing something... task: 3 doing something... task: 1 doing something... task: 21 doing something... task: 2 doing something... task: 31 doing something... main exit... task: 11 doing something... task: 11 exit... task: 21 doing something... task: 21 exit... task: 3 doing something... task: 3 exit... task: 31 doing something... task: 31 exit... task: 1 doing something... task: 1 exit... task: 2 doing something... task: 2 exit...