一、Go 如何让子程序自动退出

func main() {
	// context.WithTimeout,返回一个实现context的结构体,有超时返回的功能
	// context.Background()   是一个固定的参数,表示根节点
	// 5*time.Second  表示设置5秒超时,超过两秒子程序会自动退出。
	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)

	// 开启协程
	go worker2(ctx)

	// 主程序等待五秒,防止自动退出
	// 当在函数前加 go 意味着这个函数开了一个协程执行函数,主线程会跳过这段
	// 当线程结束,不管字协程是否结束,会自动结束,所以我们等待10秒,字协程等待五秒主线程也不会结束。
	time.Sleep(time.Second * 10)

	fmt.Println("主线程结束")
}

worker2函数设定一个for无限循环函数,只有当 ctx.Done()通道有值的时候才会触发break。

func worker2(ctx context.Context) {
	// break Loop 跳到这里,并且跳过for{}
LOOP:
	for {
		select {
		// ctx 传入的时候第二个参数设定了5秒 time.Second,当时间到了,ctx.Done(一个通道),通道中会传入值,
		// 这样 <-ctx.Done() 就能执行力,否则 select就一直执行default{}
		case <-ctx.Done():
			fmt.Println("ctx.Done()通道经过5秒接受到信息,退出")
			break LOOP
		default:
			fmt.Println("执行default,并停止1秒")
			time.Sleep(time.Second)
		}
	}
}


二、Go 如何让主协程控制子协程退出

主线程调用canf()方法,子协程就会自动退出了。

func main() {
// 上面采用了 context.WithTimeout方法,这次使用 context.WithCancel,并让左边的 _ 改成canf
	ctx, canf := context.WithCancel(context.Background())

	// 开启协程
	go workerWithCancel(ctx)

	//主程序等待五秒,防止自动退出
	//当在函数前加 go 意味着这个函数开了一个协程执行函数,主线程会跳过这段
	//当线程结束,不管字协程是否结束,会自动结束,所以我们等待10秒,字协程等待五秒主线程也不会结束。
	time.Sleep(time.Second * 2)

	// 调用canf函数,ctx.Done() 通道自己会赋值,这样子协程就会break了
	canf()

	fmt.Println("主线程结束")
}


func workerWithCancel(ctx context.Context) {
	// break Loop 跳到这里,并且跳过for{}
LOOP:
	for {
		select {
		// 当main调用WithCancel的返回参数canf,canf调用的时候,ctx.Done(一个通道),通道中会传入值
		// 这样 <-ctx.Done() 就能执行力,否则 select就一直执行default{}
		case <-ctx.Done():
			fmt.Println("ctx.Done()通道经过5秒接受到信息,退出")
			break LOOP
		default:
			fmt.Println("执行default,并停止1秒")
			time.Sleep(time.Second)
		}
	}
}