golang context使用

context

context是go并发模式,用户处理goroutines和其父亲之间逻辑。比如接受一个用户的请求后,针对这个请求可能需要开启多个并发的goroutine,如何把这些goroutine和这个client进行关联?就是通过context,如果用户取消请求,所有的goroutine应该能感知,并自动结束。同时可以将共用的变量存储在context之中,供不同的goroutine使用。

从一个程序说起

func main() {
	out := make(chan int)
	go func() {
		i := 0
		for i < 3 {
			out <- i
			fmt.Println("Send ", i)
			i = i + 1
		}
		close(out)
	}()

	fmt.Println(<-out)
	time.Sleep(1 * time.Minute)
}

退出的时候,sub goroutine还没结束,存在资源泄露的情况。

使用context协调sub goroutine

func test() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	out := make(chan int)
	go func() {
		i := 0
		for i < 3 {
			select {
			case out <- i:
				fmt.Println("Send ", i)
				i = i + 1
			case <-ctx.Done():
				fmt.Println("Done") fmt.Println(ctx.Err())
				return
			}
		}
		close(out)
	}()

	fmt.Println(<-out)
	fmt.Println("cancal being call")
}

func main() {
	test()
	time.Sleep(5 * time.Second)
}

使用timeout自动执行cancel

func test() {
	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
	out := make(chan int)
	go func() {
		i := 0
		for i < 3 {
			select {
			case out <- i:
				fmt.Println("Send ", i)
				i = i + 1
			case <-ctx.Done():
				fmt.Println("Done")
				fmt.Println(ctx.Err())
				return
			}
		}
		close(out)
	}()

	fmt.Println(<-out)
	for i := 1; i <= 10; {
		time.Sleep(1 * time.Second)
		fmt.Println("wait for ", i, " sec ")
		i = i + 1
	}
}

func main() {
	test()
}

相关链接

  1. context blog
  2. example code