Stop goroutine execution on timeout

纵饮孤独 提交于 2019-12-19 19:38:45

问题


I want to stop goroutine execution on timeout. But it seems like it is not working for me. I am using iris framework.

  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
    }

  }

Output:

 Timed out
 test
 test1

Expected Output:

 Timed out

Can somebody point out what is missing here? It does timeout but still runs goroutine to print test and test1. I just want to stop the execution of goroutine as soon as there is timeout.


回答1:


There's no good way to "interrupt" the execution of a goroutine in the middle of it's execution.

Go uses a fork-join model of concurrency, this means that you "fork" creating a new goroutine and then have no control over how that goroutine is scheduled until you get to a "join point". A join point is some kind of synchronisation between more than one goroutine. e.g. sending a value on a channel.

Taking your specific example, this line:

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

... will be able to send the value, no matter what because it's a buffered channel. But the timeout's you've created:

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

These timeouts are on the "receiver" or "reader" of the channel, and not on the "sender". As mentioned at the top of this answer, there's no way to interrupt the execution of a goroutine without using some synchronisation techniques.

Because the timeouts are on the goroutine "reading" from the channel, there's nothing to stop the execution of the goroutine that send on the channel.




回答2:


Best way to control your goroutine processing is context (std go library).

You can cancel something inside goroutine and stop execution without possible goroutine leak.

Here simple example with cancel by timeout for your case.

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")
}



回答3:


You have a gouroutine leaks, you must handle some done action to return the goroutine before timeout like this:

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
    }

}


来源:https://stackoverflow.com/questions/50570961/stop-goroutine-execution-on-timeout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!