Golang中context的基本使用

1.0 问题引入

\qquad 作为golang的学习者,尤其是对于没有什么开发经验的学生,可能对于golang中一些并发的库比较难理解,需要一些具体的场景帮助理解:
\qquad 首先看下面一段代码:

func hello() {
	fmt.Println("hello,world")
	go hello_loop()
	time.Sleep(time.Second * 5)
	fmt.Println("hello exit")
}

func hello_loop() {
	for range time.Tick(time.Second) {
		fmt.Println("running")
	}
}

func main() {
	go hello()
	time.Sleep(20 * time.Second)
}

\qquad main函数退出时会结束所有正在执行的函数,这是基本的常识,但在服务运行时,main函数实际永远在运行中,所以我们用time.sleep()来模拟服务的运行过程,当用户需要业务时,服务通过创建新的goroutine来为用户服务,这里使用hello()来模拟
\qquad 这段代码输出的结果显而易见

hello,world
running
running
running
running
running
hello exit
running
running
running

\qquad 可以看出虽然在函数调用(协程启动)是具有父子关系,但是子协程并不会随着父线程的退出而退出,这一点在实际开发中极易造成协程泄漏与死锁,也就是协程在后台无序运行或者被永久挂起,最终造成服务崩溃,因此需要对协程进行管理,而golang中的context库可以完成这一任务。

1.1 基本原理

\qquad 程序中协程的开启本身是一种树结构,但自然情况下协程一旦启动不会受到父协程的管理,完全处于一种无序状态
\qquad context的本意是上下文,放在这里就是想让协程开启时能够携带上下文信息,从而知道自己“父亲”的运行状态,从而接受他的管理,因此我们想在开启协程的同时,同步的构建一种表示协程关系树结构,来统一的管理各个协程。
\qquad 下面我们来对上面的代码进行改造,让大家直接感受到context库的作用:

func hello() {
	//这一段是加入的代码
	ctx, cancle := context.WithCancel(context.Background())
	defer cancle() // 在函数退出时调用cancle()
	//
	fmt.Println("hello,world")
	go hello_loop(ctx)
	time.Sleep(time.Second * 5)
	fmt.Println("hello exit")
}

func hello_loop(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-time.After(time.Second):
			fmt.Println("running")
		}
	}
}

func main() {
	go hello()
	time.Sleep(20 * time.Second)
}

\qquad 这里的结果就不再赘述,进行改造后,子协程可以随着父协程的结束而退出,从而实现了协程的管理
\qquad 基本原理是我们将一个父协程创建的context.Context类型的变量在创建子协程时传入子协程中,而这个变量中记录了父协程的运行状态(是否调用了cancel()方法)
\qquad 这里注意代码中只实现了一层的结构,实际上context.WithCancel()方法中的输出与输出都是context.Context类型,因此可以嵌套使用,从而组成协程树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值