go - 为什么当我超时函数时不调用延迟?

标签 go concurrency

当我在函数中添加延迟时,我希望它在函数结束时始终被调用。 我注意到当函数超时时它不会发生。

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() 中的延迟从未被调用,这就是输出的原因:

service 1 -> false
service 2 -> true

代替

service 1 -> false
service 2 -> false

我知道超时意味着“停止执行”,但对我来说执行延迟代码是合理的。我找不到对此行为的任何解释。

问题的第二部分 - 如何修改服务或 Registry 以抵抗这种情况?

最佳答案

第一部分的答案

假设您有一个函数 f1(),它使用 defer 来调用 f2(),即 defer f2() 。事实上,即使发生运行时 panic ,当且仅当 f1 完成时,才会调用 f2。更具体地说,看go-defer .

现在我们关心的是在 goroutine 中使用 defer。我们还必须记住,如果 go-routine 的父函数完成退出,它就会退出。

因此,如果我们在 go-routine 函数中使用 defer,那么如果父函数完成或退出,则 go-routine 函数必须退出。由于它退出(未完成),defer 语句将不会执行。很明显,我们绘制了您程序的状态。 enter image description here 如您所见,

  • 在第 1 毫秒,service1() 先于其他服务完成。因此,service2() 会在不执行 defer 语句的情况下退出,并且“service 2”不会被设置为 false。由于 service1() 完成,它的 defer 将执行并且“服务 1”将设置为 false
  • 在第 2 毫秒,main() 完成,程序结束。

所以我们看看这个程序是如何执行的。

第二部分的答案

我尝试过的一个可能的解决方案是增加 service1() 的时间或减少 service2() 的时间。

关于go - 为什么当我超时函数时不调用延迟?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53166848/

相关文章:

templates - 带有开关和 ForEach 的 Golang 模板

go - 将来自多个 go routines 的响应获取到一个数组中

c++ - 使用多线程优化执行时间(简单示例)

eclipse - SSH(FTP)和Web服务器并发文件IO

go - 如何在golang中解压缩grpc status.details错误?

go - 复制 slice 有什么意义?

go - 类型导入周期

java - 在 Java 中使用 ThreadLocal 的良好实践

wcf - 使用 ConcurrencyMode.Multiple 模式保留 WCF 服务中的消息顺序

mongodb - 查询值小于X的查询未返回任何内容