Goroutine Timeout

若如初见. 提交于 2021-02-05 11:30:35

问题


type Response struct {
  data   interface{}
  status bool
}

func Find() (interface{}, bool) {
  ch := make(chan Response, 1)

  go func() {
    data, status := findCicCode()
    ch <- Response{data: data, status: status}
  }()

  select {
  case response := <-ch:
    return response.data, response.status
  case <-time.After(50 * time.Millisecond):
    return "Request timed out", false
  }
}

So, I have above function. Basically findCicCode() function call makes 3 http calls internally to external services. I have added combined timeout here for those 3 http calls. Can't put individual timeout in my case. But It still makes api calls in background if it exceeds timeout.

I am not sure if there is goroutine leak here. Is there a way to cancel those https requests if there is timeout?


回答1:


You control cancelation of http requests with a context.Context.

// create a timeout or cancelation context to suit your requirements
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

req, err := http.NewRequest("GET", location, nil)

// add the context to each request and they will be canceled in unison
resp, err := http.Do(req.WithContext(ctx))



回答2:


If you want to, you can create your own timeout system for arbitrary work by having a single receive operation on the channel (in the main goroutine), and whichever other goroutine reaches its send operation first -- the time.Sleep or the one doing actual work -- wins.

Here's a complete runnable example/simulation. Adjust timeout and delay values to simulate different scenarios. The channel is unbuffered, and is closed after a single value is read to allow the other goroutine to exit on send.

package main

import(
    "fmt"
    "time"
)

type Response struct {
    Data        []byte
    Status      int
}

func Wait(s int) {
    time.Sleep(time.Duration(s) * time.Second)
}

func FindWrapper(ch chan Response, delay int) {
    // Put real find stuff here...

    // Dummy response after wait for testing purposes
    Wait(delay)
    ch <- Response{[]byte("Some data..."), 200}
}

func main() {
    timeout := 3
    delay := 4
    ch := make(chan Response)

    // whoever sends to ch first wins...
    go func() {
        Wait(timeout)
        ch <- Response{}
    }()
    go FindWrapper(ch, delay)

    r := <-ch
    close(ch)
    if r.Data == nil {
        r.Status = 500 // or whatever you want for timeout status
    }
    fmt.Printf("Data: %s  Status: %d\n", string(r.Data), r.Status)
}

A buffered channel would work too. And you could accomplish the same thing with a sync.WaitGroup where you only call Add once and then close the channel after wg.Wait().

That said, I suggest trying JimB's solution of using a Context timeout, as it probably works for your use case and is a less complicated solution.



来源:https://stackoverflow.com/questions/51223062/goroutine-timeout

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