如果等待goroutine的结果,我应该关闭时间通道吗?

j91ykkif  于 2023-04-18  发布在  Go
关注(0)|答案(2)|浏览(130)

让我们假设以下代码

type resultWrapper struct {
  result Result
  err    error
}

func (e executor) Execute(ctx context.Context) Result {
  ch := make(chan ResultWrapper, 1)

  go func() {
      defer close(ch)
      // base also returns type Result
      res := e.base.Execute(ctx)
      if res.IsError() {
          ch <- resultWrapper{
              result: &e.defaultResponse,
              err:    nil,
          }
          return
      }
      ch <- wrapper{
          result: &res,
          err:    nil,
      }
  }()

  select {
  case <-time.After(e.timeout):
      return e.defaultResponse
  case res := <-ch:
      return res.result
  }
}

我的问题是我是否应该以某种适当的方式关闭由time.After通道创建的通道?我不完全理解如果我超时退出主Execute函数,goroutine调用e.base.Execute会发生什么?

vdgimpew

vdgimpew1#

time.After()返回一个只接收通道:

func After(d Duration) <-chan Time

不能关闭,引用自规格:内置功能:关闭:
如果参数ch的核心类型为channel,则内置函数close会记录不再在该channel上发送值。如果ch是只接收通道,则会出错。
time.After()的文档还指出:
在计时器触发之前,垃圾回收器不会恢复基础计时器。如果需要提高效率,请改用NewTimer并调用Timer。如果不再需要计时器,请停止。
因此,您可以使用time.NewTimer(),之前您可以使用Timer.Stop()“释放”它。

**但是!**由于您调用的操作e.base.Execute(ctx)已经支持上下文,因此最简单的解决方案是创建一个新的、带超时的派生上下文(使用context.WithTimeout()):

func (e executor) Execute(ctx context.Context) Result {
    ctx, cancel := context.WithTimeout(ctx, e.timeout)
    defer cancel()

    res := e.base.Execute(ctx)
    if res.IsError() {
            return e.defaultResponse,
    }

    return res.result
}

如果e.base.Execute()正确实现,它必须监视传递的上下文,这将在e.timeout之后被取消。注意延迟的cancel()调用,如果e.base.Execute()e.timeout之前完成,它将释放已使用的资源。

1l5u6lss

1l5u6lss2#

不幸的是,你不能停止time.After的goroutine,如果你担心泄露长期存在的例程,最好使用time.timer,它可以被停止。

type resultWrapper struct {
  result Result
  err    error
}

func (e executor) Execute(ctx context.Context) Result {
  ch := make(chan ResultWrapper, 1)

  go func() {
      defer close(ch)
      // base also returns type Result
      res := e.base.Execute(ctx)
      if res.IsError() {
          ch <- resultWrapper{
              result: &e.defaultResponse,
              err:    nil,
          }
          return
      }
      ch <- wrapper{
          result: &res,
          err:    nil,
      }
  }()

  t := time.NewTimer(e.timeout)
  defer t.Stop()

  select {
  case <-t.C:
      return e.defaultResponse
  case res := <-ch:
      return res.result
  }
}

相关问题