当一个goroutine可以启动其他goroutine,而这些goroutine可以启动其他goroutine,依此类推,则第一个goroutine应该能够向所有其它goroutine发送取消信号。
上下文包的唯一目的是在goroutine之间执行取消信号,而不管它们如何生成。上下文的接口定义为:
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <- chan struct{} Err() error Value(key interface{}) interface{} }
- Deadline:第一个值是截止日期,此时上下文将自动触发“取消”操作。第二个值是布尔值,true表示设置了截止日期,false表示未设置截止时间。如果没有设置截止日期,则必须手动调用cancel函数来取消上下文。
- Done:返回一个只读通道(仅在取消后),键入struct {},当该通道可读时,表示父上下文已经发起了取消请求,根据此信号,开发人员可以执行一些清除操作,退出goroutine
- Err:返回取消上下文的原因
- Value:返回绑定到上下文的值,它是一个键值对,因此您需要传递一个Key来获取相应的值,此值是线程安全的
要创建上下文,必须指定父上下文。两个内置上下文(背景和待办事项)用作顶级父上下文:
var ( background = new(emptyCtx) todo = new(emptyCtx) ) func Background() Context { return background } func TODO() Context { return todo }
背景,主要ü在主函数,初始化和测试代码的sed,是树结构中,根上下文,这是不能被取消的顶层语境。TODO,当您不知道要使用什么上下文时,可以使用它。它们本质上都是emptyCtx类型,都是不可取消的,没有固定的期限,也没有为Context赋任何值:键入emptyCtx int
type emptyCtx int func (_ *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(父上下文)(ctx上下文,取消CancelFunc)func WithDeadline(父上下文,截止时间.Time)(上下文,CancelFunc)func WithTimeout(父上下文,超时时间。持续时间)(上下文,CancelFunc)func WithValue(父上下文,键,val接口{})上下文
请注意,这些方法意味着可以一次继承上下文以实现其他功能,例如,使用WithCancel函数传入根上下文,它会创建一个子上下文,该子上下文具有取消上下文的附加功能,然后使用此方法将context(context01)作为父上下文,并将其作为第一个参数传递给WithDeadline函数,与子context(context01)相比,获得子context(context02),它具有一个附加功能,可在之后自动取消上下文最后期限。
WithCancel
对于通道,尽管通道也可以通知许多嵌套的goroutine退出,但通道不是线程安全的,而上下文是线程安全的。
例如:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ch chan bool, index int) { for { select { case v := <- ch: fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor2: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func monitor1(ch chan bool, index int) { for { go monitor2(ch, index) select { case v := <- ch: // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor1: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func main() { var stopSingal chan bool = make(chan bool, 0) for i := 1; i <= 5; i = i + 1 { go monitor1(stopSingal, i) } time.Sleep(1 * time.Second) // close all gourtines cancel() // waiting 10 seconds, if the screen does not display <monitorX: xxxx in progress...>, all goroutines have been shut down time.Sleep(10 * time.Second) println(runtime.NumGoroutine()) println("main program exit!!!!") }
执行的结果是:
monitor1: 5 in progress...
monitor2: 5 in progress...
monitor1: 2 in progress...
monitor2: 2 in progress...
monitor2: 1 in progress...
monitor1: 1 in progress...
monitor1: 4 in progress...
monitor1: 3 in progress...
monitor2: 4 in progress...
monitor2: 3 in progress...
monitor1: 4, the received channel value is: false, ending
monitor1: 3, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 1, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 5, the received channel value is: false, ending
monitor1: 2, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
1
main program exit!!!!
这里使用一个通道向所有goroutine发送结束通知,但是这里的情况相对简单,如果在一个复杂的项目中,假设多个goroutine有某种错误并重复执行,则可以重复关闭或关闭该通道通道,然后向其写入值,从而触发运行时恐慌。这就是为什么我们使用上下文来避免这些问题的原因,以WithCancel为例:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ctx context.Context, number int) { for { select { case v := <- ctx.Done(): fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number,v) return default: fmt.Printf("monitor: %v in progress...\n", number) time.Sleep(2 * time.Second) } } } func monitor1(ctx context.Context, number int) { for { go monitor2(ctx, number) select { case v := <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number, v) return default: fmt.Printf("monitor: %v in progress...\n", number) time.Sleep(2 * time.Second) } } } func main() { var ctx context.Context = nil var cancel context.CancelFunc = nil ctx, cancel = context.WithCancel(context.Background()) for i := 1; i <= 5; i = i + 1 { go monitor1(ctx, i) } time.Sleep(1 * time.Second) // close all gourtines cancel() // waiting 10 seconds, if the screen does not display <monitor: xxxx in progress>, all goroutines have been shut down time.Sleep(10 * time.Second) println(runtime.NumGoroutine()) println("main program exit!!!!") }
WithTimeout和WithDeadline
WithTimeout和WithDeadline在用法和功能上基本相同,它们都表示上下文将在一定时间后自动取消,唯一的区别可以从函数的定义中看出,传递给WithDeadline的第二个参数是类型time.Duration类型,它是一个相对时间,表示取消超时后的时间。例:
package main import ( "runtime" "fmt" "time" "context" ) func monitor2(ctx context.Context, index int) { for { select { case v := <- ctx.Done(): fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor2: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func monitor1(ctx context.Context, index int) { for { go monitor2(ctx, index) select { case v := <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v) return default: fmt.Printf("monitor1: %v in progress...\n", index) time.Sleep(2 * time.Second) } } } func main() { var ctx01 context.Context = nil var ctx02 context.Context = nil var cancel context.CancelFunc = nil ctx01, cancel = context.WithCancel(context.Background()) ctx02, cancel = context.WithDeadline(ctx01, time.Now().Add(1 * time.Second)) // If it's WithTimeout, just change this line to "ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)" defer cancel() for i := 1; i <= 5; i = i + 1 { go monitor1(ctx02, i) } time.Sleep(5 * time.Second) if ctx02.Err() != nil { fmt.Println("the cause of cancel is: ", ctx02.Err()) } println(runtime.NumGoroutine()) println("main program exit!!!!") }
WithValue
一些必需的元数据也可以通过上下文传递,该上下文将附加到上下文中以供使用。元数据作为键值传递,但请注意,键必须具有可比性,并且值必须是线程安全的。
package main import ( "runtime" "fmt" "time" "context" ) func monitor(ctx context.Context, index int) { for { select { case <- ctx.Done(): // this branch is only reached when the ch channel is closed, or when data is sent(either true or false) fmt.Printf("monitor %v, end of monitoring. \n", index) return default: var value interface{} = ctx.Value("Nets") fmt.Printf("monitor %v, is monitoring %v\n", index, value) time.Sleep(2 * time.Second) } } } func main() { var ctx01 context.Context = nil var ctx02 context.Context = nil var cancel context.CancelFunc = nil ctx01, cancel = context.WithCancel(context.Background()) ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second) var ctx03 context.Context = context.WithValue(ctx02, "Nets", "Champion") // key: "Nets", value: "Champion" defer cancel() for i := 1; i <= 5; i = i + 1 { go monitor(ctx03, i) } time.Sleep(5 * time.Second) if ctx02.Err() != nil { fmt.Println("the cause of cancel is: ", ctx02.Err()) } println(runtime.NumGoroutine()) println("main program exit!!!!") }
关于上下文,还有一些注意事项:不要将Context存储在结构类型中,而是将Context明确传递给需要它的每个函数,并且Context应该是第一个参数。
即使函数允许,也不要传递nil Context,或者如果您不确定要使用哪个Context,请传递context。不要将可能作为函数参数传递给上下文值的变量传递。
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 群星《燃!沙排少女 影视原声带》[FLAC/分轨][775.28MB]
- 群星《第6届2010十大发烧唱片精选》2CD [WAV+CUE][1.6G]
- 窦唯1994《黑梦》上海音像首版[WAV分轨][1G]
- 郭子.1996-为爱偷生(载歌载舞歌载戏“极度疯狂”唱作全纪录)【滚石】【WAV+CUE】
- 伍佰.2003-泪桥【艾回】【WAV+CUE】
- 南台湾小姑娘.1996-爱作梦的查某囡仔【大旗】【WAV+CUE】
- 群星《天碟落地-世界[HI-FI] 女声》[WAV分轨][1.1G]
- 黎明《但愿不只是朋友》2022蜚声环球限量版 [WAV+CUE][1G]
- 李玉刚《怀旧辑》玉泽东方[WAV+CUE][1.1G]
- 魔兽世界wlk刺杀贼一键输出宏是什么 wlk刺杀贼一键输出宏介绍
- 魔兽世界wlk战斗贼一键输出宏是什么 wlk战斗贼一键输出宏介绍
- 魔兽世界wlk敏锐贼一键输出宏是什么 wlk敏锐贼一键输出宏介绍
- 李逸朗2007-李威乐[英皇娱乐][WAV+CUE]
- DavidVersace-EyetoEye(2024)[24-44,1]
- SimoneLamsma,RobertKulek-Mendelssohn,Janacek,Schumann-SonatasforViolinandPiano【SAC