Golang WaitGroup超时错误处理

In the following code, how can I add proper timeout error handling in case one of the launched go routines takes too long (e.g. > 10 sec) to finish? Note, that I do not want to have an "overall" timeout but a timeout for each go routine, so that I can also know which go routine timed out in my error report.

var wg sync.WaitGroup

for _, element:= range elements{
    wg.Add(1)
    go doWork(element, &wg)
}
wg.Wait()

kind regards

nice way is to use context.WithDeadline:

// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))

    // Even though ctx will be expired, it is good practice to call its
    // cancelation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err())
    }

}

You can use Context, in the following way:

func doWork(ctx context.Context, element Element, wg &sync.WaitGroup) {
    defer wg.Done()

    done := make(chan struct{})
    go func() {
       // do some work on element
       done <- struct{}{} // signal work is done
    }

    select {
       case <- done: 
       { 
          // work completed in time
       }
       case <- ctx.Done:
       {
         // timeout reached
       }
    }
}

contexts := make([]*context.Context, len(elements))

for _, element:= range elements{
    wg.Add(1)
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()
    contexts = append(contexts, ctx)
    go doWork(ctx, element, &wg)
}
wg.Wait()

for i, ctx := range contexts {
  if ctx.Err() {
     fmt.Println("Go routine ", i, "canceled due to", ctx.Err())
  }
}

I indeed had the same question and came up with this approach:

https://play.golang.org/p/9F9T_sYIof

Using: context.WithTimeout(context.Background(), 10*time.Second):

https://play.golang.org/p/WK0ebe0c9t

Don't know if is the proper way of doing it but is working:

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func doWork(element int, wg *sync.WaitGroup) {
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
    defer cancel()

    ch := make(chan struct{})

    go func(ch chan struct{}) {
        time.Sleep(time.Second)
        fmt.Printf("element = %+v
", element)
        ch <- struct{}{}
    }(ch)

    select {
    case <-ch:
    case <-ctx.Done():
        fmt.Println(ctx.Err())
    }
    wg.Done()
}

func main() {
    var wg sync.WaitGroup

    elements := []int{1, 2, 3}

    for _, element := range elements {
        wg.Add(1)
        go doWork(element, &wg)
    }
    wg.Wait()
}

Notice the goroutine within the doWork function:

go func(ch chan struct{}) {
   // your code logic goes here
}(ch)

That's the part that I don't know if is the best way of doing it, but seems to be the pattern to follow when using context mainly when want to deal with the ctx.Done()