当我在函数中添加延迟时,我希望它在函数结束时始终被调用。我注意到当函数超时时它不会发生。package mainimport ( "context" "fmt" "time")func service1(ctx context.Context, r *Registry) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer func() { r.Unset("service 1") }() r.Set("service 1") go service2(ctx, r) select { case <-ctx.Done(): cancel() break } } func service2(ctx context.Context, r *Registry) { defer func() { r.Unset("service 2") }() r.Set("service 2") time.Sleep(time.Millisecond * 300) } type Registry struct { entries map[string]bool } func (r *Registry)Set(key string) { r.entries[key] = true } func (r *Registry)Unset(key string) { r.entries[key] = false } func (r *Registry)Print() { for key, val := range r.entries { fmt.Printf("%s -> %v\n", key, val) } } func NewRegistry() *Registry { r := Registry{} r.entries = make(map[string]bool) return &r }func main() { r := NewRegistry() ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200) go service1(ctx, r) // go service3(ctx, r) select { case <-ctx.Done(): fmt.Printf("context err: %s\n", ctx.Err()) cancel() } r.Print() }在上面的示例中,永远不会调用 defer in service2(),这就是输出的原因:service 1 -> falseservice 2 -> true代替service 1 -> falseservice 2 -> false我知道超时意味着“停止执行”,但对我来说执行延迟代码是合理的。我找不到对此行为的任何解释。问题的第二部分 - 如何修改服务或Registry抵抗这种情况?
1 回答
手掌心
TA贡献1942条经验 获得超3个赞
第一部分的答案
f1()
假设您有一个用于defer
调用的函数f2()
,即defer f2()
。事实上,即使发生运行时恐慌,f2
当且仅当完成时才会调用。
现在我们关心的是在 goroutine 中使用 defer。我们还必须记住,如果 go-routine 的父函数完成退出,它就会退出。
因此,如果我们defer
在一个 go-routine 函数中使用,那么如果父函数完成或退出,则 go-routine 函数必须退出。由于它退出(未完成),该defer
语句将不会执行。很明显,我们绘制了您程序的状态。
如你所见,
在第 1 毫秒,
service1()
先于其他人完成。因此,service2()
不执行defer
语句就退出,并且“服务 2”不会设置为false
. 完成后service1()
,它将defer
执行并将“服务 1”设置为false
。在第 2 毫秒,
main()
完成并程序结束。
所以我们看看这个程序是如何执行的。
第二部分的答案
我尝试过的一种可能的解决方案是增加时间service1()
或减少时间service2()
。
- 1 回答
- 0 关注
- 96 浏览
添加回答
举报
0/150
提交
取消