context.WithDeadline while passing context to go routine?

Issue

context.WithDeadline while passing context to go routine?

I have put together some sample code that will start a new goroutine for every item in my slice.
At the moment, this will wait for the done channel to be called len(slice) times.

However, I would also like to implement a timeout in the goroutines to event leaking.
It seems that context.WithDeadline (or maybe WithTimeout?)is the appropriate function to use.

For example, lets say I want to pass in a 23 second deadline for all goroutines that are initialized from main().
However, its not clear to me how I should do this.

I have read godoc along with Go Concurrency Patterns: Context (on the go blog) but as a new gopher,
I am none the wiser. Many of the examples that I have found use http.handler(or similar as examples and so they are a source of some confusion for me.

What is an appropriate way to pass in context with deadline / timeout here.

package main

import (
  "fmt"
  "time"
)

func sleepNow(i int, done chan bool) {
  time.Sleep(time.Duration(i) * time.Second)
  fmt.Println(i, "has just woken up from sleep and the time is", time.Now())

  done <- true
}

func main() {
  done := make(chan bool)
  numbersSlice := []int{10, 20, 30, 12}

  for _, v := range(numbersSlice){
    go sleepNow(v, done)
  }

  for x := 0; x < len(numbersSlice); x++ {
  <-done
  }
  fmt.Println("Looks like we are all done here!")

}

Solution

All you need to do is get the context into the function where you want to use it. I many cases you can use a simple closure, or in this case, add it to the function arguments.

Once you have the context in place, you can select on the Context.Done() channel to determine when it has expired.

https://play.golang.org/p/q-n_2mIW2X

func sleepNow(i int, ctx context.Context, wg *sync.WaitGroup) {
    defer wg.Done()

    select {
    case <-time.After(time.Duration(i) * time.Second):
        fmt.Println(i, "has just woken up from sleep and the time is", time.Now())
    case <-ctx.Done():
        fmt.Println(i, "has just been canceled")
    }
}

func main() {
    var wg sync.WaitGroup
    numbersSlice := []int{1, 5, 4, 2}

    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)

    for _, v := range numbersSlice {
        wg.Add(1)
        go sleepNow(v, ctx, &wg)
    }

    wg.Wait()
    cancel()

    fmt.Println("Looks like we are all done here!")
}

You should also use a sync.WaitGroup rather than rely on counting tokens over channel, and use defer to call Done.

Answered By – JimB

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.