func main() {var wg sync.WaitGroupwg.Add(2)go func() {time.Sleep(2*time.Second)fmt.Println("1号完成")wg.Done()}()go func() {time.Sleep(2*time.Second)fmt.Println("2号完成")wg.Done()}()wg.Wait()fmt.Println("好了,大家都干完了,放工")}
func main() {stop := make(chan bool)go func() {for {select {case <-stop:fmt.Println("监控退出,停止了...")returndefault:fmt.Println("goroutine监控中...")time.Sleep(2 * time.Second)}}}()time.Sleep(10 * time.Second)fmt.Println("可以了,通知监控停止")stop<- true//为了检测监控过是否停止,如果没有监控输出,就表示停止了time.Sleep(5 * time.Second)}
func main() {ctx, cancel := context.WithCancel(context.Background())go func(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("监控退出,停止了...")returndefault:fmt.Println("goroutine监控中...")time.Sleep(2 * time.Second)}}}(ctx)time.Sleep(10 * time.Second)fmt.Println("可以了,通知监控停止")cancel()//为了检测监控过是否停止,如果没有监控输出,就表示停止了time.Sleep(5 * time.Second)}
func main() {ctx, cancel := context.WithCancel(context.Background())go watch(ctx,"【监控1】")go watch(ctx,"【监控2】")go watch(ctx,"【监控3】")time.Sleep(10 * time.Second)fmt.Println("可以了,通知监控停止")cancel()//为了检测监控过是否停止,如果没有监控输出,就表示停止了time.Sleep(5 * time.Second)}func watch(ctx context.Context, name string) {for {select {case <-ctx.Done():fmt.Println(name,"监控退出,停止了...")returndefault:fmt.Println(name,"goroutine监控中...")time.Sleep(2 * time.Second)}}}
type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}}
Deadline方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。
Done方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。
Err方法返回取消的错误原因,因为什么Context被取消。
Value方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。
var (background = new(emptyCtx)todo = new(emptyCtx))func Background() Context {return background}func TODO() Context {return todo}
type emptyCtx intfunc (*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(parent Context) (ctx Context, cancel CancelFunc)func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)func WithValue(parent Context, key, val interface{}) Context
WithCancel函数,传递一个父Context作为参数,返回子Context,以及一个取消函数用来取消Context。
WithDeadline函数,和WithCancel差不多,它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。
WithTimeout和WithDeadline基本上一样,这个表示是超时自动取消,是多少时间后自动取消Context的意思。
WithValue函数和取消Context无关,它是为了生成一个绑定了一个键值对数据的Context,这个绑定的数据可以通过Context.Value方法访问到,后面我们会专门讲。
var key string="name"func main() {ctx, cancel := context.WithCancel(context.Background())//附加值valueCtx:=context.WithValue(ctx,key,"【监控1】")go watch(valueCtx)time.Sleep(10 * time.Second)fmt.Println("可以了,通知监控停止")cancel()//为了检测监控过是否停止,如果没有监控输出,就表示停止了time.Sleep(5 * time.Second)}func watch(ctx context.Context) {for {select {case <-ctx.Done()://取出值fmt.Println(ctx.Value(key),"监控退出,停止了...")returndefault://取出值fmt.Println(ctx.Value(key),"goroutine监控中...")time.Sleep(2 * time.Second)}}}
func Stream(ctx context.Context, out chan<- Value) error {for {v, err := DoSomething(ctx)if err != nil {return err}select {case <-ctx.Done():return ctx.Err()case out <- v:}}}
package mainimport ("context""fmt""math/rand""time")// 作用:1.随机sleep一会;2.如果入参ch不为空,会把sleep的时间给到chfunc sleepRandom(fromFunction string, ch chan int) {defer func() { fmt.Println(fromFunction, "sleepRandom complete") }()seed := time.Now().UnixNano()r := rand.New(rand.NewSource(seed))randomNumber := r.Intn(100)sleeptime := randomNumber + 100fmt.Println(fromFunction, "Starting sleep for", sleeptime, "ms")time.Sleep(time.Duration(sleeptime) * time.Millisecond)fmt.Println(fromFunction, "Waking up, slept for ", sleeptime, "ms")if ch != nil {ch <- sleeptime}}func sleepRandomContext(ctx context.Context, ch chan bool) {defer func() {fmt.Println("sleepRandomContext complete")// 通过channel,通知上游执行完毕ch <- true}()sleeptimeChan := make(chan int)// 开启新的协程G2,让该协程执行逻辑,执行完毕后,通过sleeptimeChan通知执行完毕go sleepRandom("sleepRandomContext", sleeptimeChan)select {case <-ctx.Done():// 场景1:main()调用cancelFunction()// 场景2:doWorkContext()调用cancelFunction()// 场景3:doWorkContext()自动超时fmt.Println("sleepRandomContext: Time to return")case sleeptime := <-sleeptimeChan:// 当新的协程G2执行完毕,调用ch<-sleeptime时fmt.Println("Slept for ", sleeptime, "ms")}}func doWorkContext(ctx context.Context) {// 生成新的ctx,超时时间为150msctxWithTimeout, cancelFunction := context.WithTimeout(ctx, time.Duration(150)*time.Millisecond)defer func() {fmt.Println("doWorkContext complete")// 下游所有的ctx都会关闭cancelFunction()}()ch := make(chan bool)// 启动新的协程G1go sleepRandomContext(ctxWithTimeout, ch)select {case <-ctx.Done():// 当main退出,调用main的cancelFunction()时fmt.Println("doWorkContext: Time to return")case <-ch:// 当新的协程G1退出,执行ch<-true时fmt.Println("sleepRandomContext returned")}}func main() {ctx := context.Background()ctxWithCancel, cancelFunction := context.WithCancel(ctx)defer func() {fmt.Println("Main Defer: canceling context")// 下游所有的ctx都会关闭cancelFunction()}()go func() {// main函数sleep一会sleepRandom("Main", nil)// 下游所有的ctx都会关闭cancelFunction()fmt.Println("Main Sleep complete. canceling context")}()doWorkContext(ctxWithCancel)}
场景1:main函数调用cancelFunction()后,main()会直接退出,同时doWorkContext和sleepRandomContext函数会同时调用里面的ctx.Done()操作,全部一起退出;
场景2:doWorkContext函数超时150ms后,sleepRandomContext函数会直接执行ctx.Done()操作,然后sleepRandomContext函数退出前执行ch <- true,doWorkContext函数接收到case <-ch的信号后,doWorkContext()退出,main()退出;
场景3:sleepRandomContext函数执行sleepRandom(),当sleepRandom执行ch <- sleeptime后,sleepRandomContext通过sleeptime := <-sleeptimeChan收到信号后,程序退出,退出前会执行ch <- true,然后doWorkContext函数接收到case <-ch的信号后,doWorkContext()退出,main()退出;
场景4:main()异常,通过defer执行cancelFunction()后,main()退出,后面逻辑同“场景1”;
场景5:doWorkContext()异常,通过defer执行cancelFunction()后,sleepRandomContext函数会直接执行ctx.Done()操作,sleepRandomContext()退出,cancelFunction()退出,main()退出;
场景6:sleepRandomContext异常,通过defer执行ch <- true,doWorkContext函数接收到case <-ch的信号后,doWorkContext()退出,main()退出;