您现在的位置是:首页 > 科技前沿

用 10 分钟了解 Go 语言 context 包使用场景

智慧创新站 2025-03-28【科技前沿】68人已围观

简介context是在Go语言1.7版才正式被公认的官方标准库内,为什么今天要介绍context使用方式呢?原因很简单,在初学Go时,写API时,常常不时就会看到在httphandler的第一个参数就会是,而这个context在这边使用的目的和含义到底是什么呢,本篇就是带大家了解什么是context,以...

context是在Go语言1.7版才正式被公认的官方标准库内,为什么今天要介绍context使用方式呢?原因很简单,在初学Go时,写API时,常常不时就会看到在httphandler的第一个参数就会是,而这个context在这边使用的目的和含义到底是什么呢,本篇就是带大家了解什么是context,以及使用的场景及方式,内容不会提及context的原码,而是用几个实际例子来了解。

使用WaitGroup

学Go时肯定要学习如何使用并发(goroutine),而开发者该如何控制并发呢?其实有两种方式,一种是WaitGroup,另一种就是context,而什么时候需要用到WaitGroup呢?很简单,就是当您需要将同一件事情拆成不同的Job进行执行,最后需要等到全部的Job都执行完成才继续执行主程序,这时候就需要用到WaitGroup,看个实际例子

packagemainimport("fmt""sync""time")funcmain(){(2)gofunc(){(2*)("job1done.")()}()gofunc(){(1*)("job2done.")()}()()("AllDone.")}

上面范例可以看到主程序穿透()来等待全部工作都执行完成,才印出最后的消息。这边会遇到一个情境就是,虽然把工作拆成多个,并且丢到背景去跑,可是使用者该如何透过其他方式来终止相关的goroutine工作呢(像是开发者都会写背景程式监控,需要连续执行)?例如UI上方有停止的按钮,点继续后,如何主动通知并且停止正在运行的Job,这边很简单,可以使用channel+select方式。

使用channel+select
packagemainimport("fmt""time")funcmain(){stop:=make(chanbool)gofunc(){for{select{case-stop:("gotthestopchannel")returndefault:("stillworking")(1*)}}}()(5*)("stopthegorutine")(5*)}

上面可以看到,透过select+channel可以快速解决这个问题,只要在任何地方将bool值丢入stopchannel就可以停止背景正在处理的Job。上述用channel来解决此问题,但是现在有个问题,假设背景有跑了无数个goroutine,或者是goroutine内又有跑goroutine呢,变得相当复杂,例如底下的状况

这边就没办法用channel方式来进行处理了,而需要用到今天的重点context。

认识context

从上图可以看到我们建立了三个worker节点来处理不同的Job,所以会在主程序最上面的宣告一个主(),然后在每个worker节点分别在单独建立子上下文中,其最主要目的就是当关闭其中一个上下文就可以直接取消该worker内在跑的Job。拿上面的例子进行改写

packagemainimport("context""fmt""time")funcmain(){ctx,cancel:=(())gofunc(){for{select{():("gotthestopchannel")returndefault:("stillworking")(1*)}}}()(5*)("stopthegorutine")cancel()(5*)}

其实可以看到只是把原本的频道换成使用context来处理,其他完全不变,这边提到使用了,使用底下方式可以扩展context

ctx,cancel:=(())

这用意在于每个worknode都有独立的cancelfunc开发者可以透过其他地方呼叫cancel()来决定哪一个工人需要被停止,这时候可以做到使用context来停止多个goroutine的效果,底下看看实际例子

packagemainimport("context""fmt""time")funcmain(){ctx,cancel:=(())goworker(ctx,"node01")goworker(ctx,"node02")goworker(ctx,"node03")(5*)("stopthegorutine")cancel()(5*)}funcworker(,namestring){for{select{():(name,"gotthestopchannel")returndefault:(name,"stillworking")(1*)}}}

上面透过一个context可以一次停止多个worker,看逻辑如何宣告context以及什么时机去执行cancel(),通常我个人都是搭配gracefulshutdown进行取消运转的工作,或者是停止资料库连线等等。。

心得

初学Go时,如果还不常使用goroutine,其实也不会理解到context的使用方式及时机,等到需要有背景处理,以及该如何停止Job,这时候才渐进了解到使用方式,当然context不只有这个使用方式,未来将会介绍其他使用方式。

很赞哦!(159)