当我在函数中添加延迟时,我希望在函数结束时将始终调用该延迟。 我注意到当函数超时时不会发生。
package main
import (
"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()
}
在上面的示例中,从未调用service2()
中的defer,这就是为什么输出为:
service 1 -> false
service 2 -> true
代替
service 1 -> false
service 2 -> false
我知道超时意味着“停止执行”,但是对我来说执行延迟的代码是合理的。我找不到这种行为的任何解释。
问题的第二部分-如何修改服务或Registry
以抵抗这种情况?
答案 0 :(得分:0)
假设您有一个函数f1()
,该函数使用defer
来调用f2()
,即defer f2()
。事实是,f2
将在且仅当f1
完成时才被调用,即使在运行时出现紧急情况时也是如此。更具体地说,请看go-defer。
现在,我们担心的是在goroutine中使用defer。我们还必须记住,如果go-routine的父函数完成了退出操作,则该例程会退出。
因此,如果我们在go-routine函数中使用defer
,则如果父函数完成或退出,则go-routine函数必须退出。由于退出(未完成),因此defer
语句将不会执行。很明显,我们绘制了程序的状态。
如您所见,
service1()
先于其他毫秒完成。因此,service2()
退出而不执行defer
语句,并且“服务2”将不会设置为false
。由于service1()
完成,将执行defer
,并将“服务1”设置为false
。main()
完成并完成编程。因此,我们了解了该程序的执行方式。
我尝试过的一种可能的解决方案是增加时间service1()
或减少时间service2()
。