【问题标题】:Stop goroutine execution on timeout超时停止 goroutine 执行
【发布时间】:2018-11-07 07:38:29
【问题描述】:

我想在超时时停止 goroutine 的执行。但它似乎对我不起作用。我正在使用iris 框架。

  type Response struct {
    data   interface{}
    status bool
  }

  func (s *CicService) Find() (interface{}, bool) {

    ch := make(chan Response, 1)

    go func() {
      time.Sleep(10 * time.Second)

      fmt.Println("test")
      fmt.Println("test1")

      ch <- Response{data: "data", status: true}
    }()

    select {
    case <-ch:
      fmt.Println("Read from ch")
      res := <-ch
      return res.data, res.status
    case <-time.After(50 * time.Millisecond):
      return "Timed out", false
    }

  }

输出:

 Timed out
 test
 test1

预期输出:

 Timed out

有人能指出这里缺少什么吗?它确实超时但仍然运行 goroutine 来打印 testtest1。我只想在超时后立即停止执行goroutine。

【问题讨论】:

    标签: go goroutine go-iris


    【解决方案1】:

    没有什么好的方法可以在执行过程中“中断”goroutine 的执行。

    Go 使用并发的 fork-join 模型,这意味着您“fork”创建一个新的 goroutine,然后在到达“join point”之前无法控制该 goroutine 的调度方式。连接点是多个 goroutine 之间的某种同步。例如在通道上发送值。

    以你的具体例子,这一行:

    ch <- Response{data: "data", status: true}
    

    ... 无论如何都可以发送值,因为它是一个缓冲通道。但是你已经创建了超时:

    case <-time.After(50 * time.Millisecond):
      return "Timed out", false
    

    这些超时在频道的“接收者”或“阅读者”上,不在在“发送者”上。如本答案顶部所述,如果不使用一些同步技术,就无法中断 goroutine 的执行。

    因为超时是在从通道“读取”的 goroutine 上,所以没有什么可以阻止在通道上发送的 goroutine 的执行。

    【讨论】:

      【解决方案2】:

      控制您的goroutine 处理 的最佳方式是context(std go 库)。

      您可以取消 goroutine 中的某些内容并停止执行不会发生 goroutine 泄漏

      这里是简单的example,并根据您的情况取消超时。

      ctx, cancel := context.WithCancel(context.Background())
      defer cancel()
      
      ch := make(chan Response, 1)
      
      go func() {
          time.Sleep(1 * time.Second)
      
          select {
          default:
              ch <- Response{data: "data", status: true}
          case <-ctx.Done():
              fmt.Println("Canceled by timeout")
              return
          }
      }()
      
      select {
      case <-ch:
          fmt.Println("Read from ch")
      case <-time.After(500 * time.Millisecond):
          fmt.Println("Timed out")
      }
      

      【讨论】:

      • 这个例子行不通。即使您将超时设置为case &lt;-time.After(50 * time.Second),您也永远无法读取ch
      • 感谢@MikeKlemin,用正确的行为更新了我的答案。
      • 此外,虽然睡眠只是“工作在这里”的一个说明,但如果确实有睡眠需求(例如在轮询周期之间等待),那么创建一个时间通道是一个好主意,例如&lt;-time.After(10*time.Second) 并将其添加到 select 语句中。这甚至允许中断睡眠。
      • 这个例子只对time.Sleep 有用。如果有一个长时间运行的函数,它不能中断它(不能提前终止)。 Example。此示例中没有打印。请选择最高投票的答案。
      • 警告:这不会停止 go 例程,直到它到达检查 ctx.Done() 的地步
      【解决方案3】:

      你有一个 gouroutine 泄漏,你必须处理一些 done 操作以在超时之前返回 goroutine,如下所示:

      func (s *CicService) Find() (interface{}, bool) {
      
          ch := make(chan Response, 1)
          done := make(chan struct{})
          go func() {
              select {
              case <-time.After(10 * time.Second):
              case <-done:
                  return
              }
              fmt.Println("test")
              fmt.Println("test1")
              ch <- Response{data: "data", status: true}
          }()
          select {
          case res := <-ch:
              return res.data, res.status
          case <-time.After(50 * time.Millisecond):
              done <- struct{}{}
              return "Timed out", false
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2013-05-04
        • 1970-01-01
        • 1970-01-01
        • 2021-11-05
        • 1970-01-01
        • 2011-10-12
        • 2017-07-19
        • 2018-04-24
        • 2020-04-03
        相关资源
        最近更新 更多