Timeout for WaitGroup.Wait ()

What is the idiomatic way to timeout WaitGroup.Wait () ?

The reason I want to do this is to protect my “planner” from the potential expectation of a “working” flight forever. This leads to some philosophical questions (for example, how can a system be reliably continued if it has strange workers?), But I think this is not suitable for this question.

I have an answer that I will provide. Now that I recorded it, it doesn't look so bad, but it still seems more confusing than it should. I would like to know if there is something available that is simpler, more idiomatic or even an alternative approach that does not use WaitGroups.

That one.

+17
concurrency go timeout
source share
5 answers

Basically your solution, which you posted below , is as good as it can. Some tips for improving it:

  • Alternatively, you can close the channel to complete the signal instead of sending a value to it, the receive operation on the closed channel can always act immediately .
  • And it is better to use the defer to complete the signal; it is executed even if the function terminates abruptly.
  • Also, if there is only one “task” for waiting, you can completely omit WaitGroup and simply send the value or close the channel after the task is completed (the same channel that you use in your select statement).
  • Specifying a duration of 1 second is as simple as: timeout := time.Second . For example, specify 2 seconds: timeout := 2 * time.Second . You do not need a conversion, time.Second already has a type of time.Duration , multiplying it by an untyped constant like 2 , you will also enter a value of type time.Duration .

I would also create a helper / utility function that wraps this functionality. Please note that WaitGroup must be passed as a pointer, otherwise the copy will not receive a “notification” of WaitGroup.Done() calls. Something like:

 // waitTimeout waits for the waitgroup for the specified max timeout. // Returns true if waiting timed out. func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { c := make(chan struct{}) go func() { defer close(c) wg.Wait() }() select { case <-c: return false // completed normally case <-time.After(timeout): return true // timed out } } 

Using it:

 if waitTimeout(&wg, time.Second) { fmt.Println("Timed out waiting for wait group") } else { fmt.Println("Wait group finished") } 

Try it on the go playground .

+35
source share

I did it like this: http://play.golang.org/p/eWv0fRlLEC

 go func() { wg.Wait() c <- struct{}{} }() timeout := time.Duration(1) * time.Second fmt.Printf("Wait for waitgroup (up to %s)\n", timeout) select { case <-c: fmt.Printf("Wait group finished\n") case <-time.After(timeout): fmt.Printf("Timed out waiting for wait group\n") } fmt.Printf("Free at last\n") 

This works great, but is this the best way to do this?

+5
source share

I wrote a library that encapsulates the concurrency logic https://github.com/shomali11/parallelizer , which you can also pass a timeout.

Here is an example without a timeout:

 func main() { group := parallelizer.DefaultGroup() group.Add(func() { for char := 'a'; char < 'a'+3; char++ { fmt.Printf("%c ", char) } }) group.Add(func() { for number := 1; number < 4; number++ { fmt.Printf("%d ", number) } }) err := group.Run() fmt.Println() fmt.Println("Done") fmt.Printf("Error: %v", err) } 

Output:

 a 1 b 2 c 3 Done Error: <nil> 

Here is a timeout example:

 func main() { options := &parallelizer.Options{Timeout: time.Second} group := parallelizer.NewGroup(options) group.Add(func() { time.Sleep(time.Minute) for char := 'a'; char < 'a'+3; char++ { fmt.Printf("%c ", char) } }) group.Add(func() { time.Sleep(time.Minute) for number := 1; number < 4; number++ { fmt.Printf("%d ", number) } }) err := group.Run() fmt.Println() fmt.Println("Done") fmt.Printf("Error: %v", err) } 

Output:

 Done Error: timeout 
0
source share

This is not a real answer to this question, but I was a (much simpler) solution to my little problem when I had this question.

My workers did http.Get () requests, so I just set the timeout on the http client.

 urls := []string{"http://1.jpg", "http://2.jpg"} wg := &sync.WaitGroup{} for _, url := range urls { wg.Add(1) go func(url string) { client := http.Client{ Timeout: time.Duration(3 * time.Second), // only want very fast responses } resp, err := client.Get(url) //... check for errors //... do something with the image when there are no errors //... wg.Done() }(url) } wg.Wait() 
0
source share

It is a bad idea. Do not abandon the routines , this can lead to races, resource leakage and unexpected conditions, which ultimately affect the stability of your application.

Instead, constantly use timeouts throughout the code to ensure that no procedures are blocked forever or run too long.

An idiomatic way to achieve this is context.WithTimeout() :

 ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second) defer cancel() // Now perform any I/O using the given ctx: go func() { err = example.Connect(ctx) if err != nil { /* handle err and exit goroutine */ } . . . }() 

Now you can safely use WaitGroup.Wait() , knowing that it will always end in a timely manner.

0
source share

All Articles