1

This Go program:

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Now()

    sleep_durations := []int{8100, 1000, 2500, 500, 6000}
    // sleep_durations := []int{8100, 1000, 2500, 500}
    c := make(chan string)
    defer close(c) // close channel when main exits
    for index, duration := range sleep_durations {
        go sleepy(fmt.Sprintf("sleepy%d: ", index+1), duration, c)
    }
    fmt.Printf("starting %d sleepys\n", len(sleep_durations))

    for range sleep_durations {
        select {
        case msg := <-c:
            fmt.Println("received: ", msg)
        case <-time.After(time.Second * time.Duration(5)):
            fmt.Println("*** TIMEOUT ***")
        }
    }

    elapsed := time.Since(start)
    fmt.Printf("... %d sleepys ran in: %e\n", len(sleep_durations), elapsed.Seconds())
}

func sleepy(msg string, sleep_ms int, yawn chan string) {
    start := time.Now()
    sleep := time.Duration(sleep_ms) * time.Millisecond
    time.Sleep(sleep) // some sleepy work
    yawn <- fmt.Sprintf("%s slept for %s", msg, sleep)
    elapsed := time.Since(start)
    fmt.Printf("\t%s finished in: %s\n", msg, elapsed)
}

https://play.golang.org/p/0ioTuKv230

has confusing results. When line 11 is uncommented it does not work as expected, i.e. the time.After 5 seconds doesn't happen. But with line 11 commented and line 12 uncommented the timeout does work as expected. I'm new to Go, but what am I missing?

4
  • You kick off a new 5-second timer each time through your loop. If you sort the second list, there are two adjacent durations (2500 and 8100) more than 5 seconds apart. Not so for the first list (the biggest gap is 2500 to 6000). If you created the time.After channel outside of the loop, you'd see the timeout code run. Whether that's the right thing to do in real life depends on your application. Commented Jul 27, 2015 at 18:34
  • You should include the code in the question and not rely on external links to make your question understandable. Commented Jul 27, 2015 at 18:36
  • It seems a lot of tutorials/blogs explain this timeout using time.After incorrectly. I got the impression it was an overall timeout, as in any goroutine taking longing than 5 seconds would trigger that case in the select. I think I should research sync.WaitGroup to handle a fan out of goroutines. Commented Jul 27, 2015 at 18:55
  • The reason behind trying to understand concurrency/timeouts in Go is to write a program that gathers social metrics for a given URL(s). Where social metrics are counts from Google+, Facebook likes/shares, LinkedIn, Pinterest, StumbleUpon, and Tweets. And to perform them in parallel (concurrently), but realizing that some api calls may take too long ... so they should be timed out. I've done this before in Ruby/Python but never concurrently. Commented Jul 27, 2015 at 19:33

2 Answers 2

2

The timeout is per-select invocation, if it keeps getting something from the channel once every 5 seconds (or less) it will keep going indefinitely.

If you meant for the timeout to apply to all operations then do so like so:

    t := time.After(5 * time.Second)
    for range sleep_durations {
        select {
        case msg := <-c:
            fmt.Println("received: ", msg)
        case <-t:
            fmt.Println("*** TIMEOUT ***")
        }
    }

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

Sign up to request clarification or add additional context in comments.

1 Comment

Try changing around the time.Duration(5) to 1 or 2. (you don't need the time.Duration() part, just a plain int). Change it to 1 and you'll see 2 timeouts, 2 seconds will have one timeout.
-1

I found what I was looking for:

package main

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

func main() {
  var wg sync.WaitGroup
  done := make(chan struct{})
  wq := make(chan interface{})
  worker_count := 2

  for i := 0; i < worker_count; i++ {
    wg.Add(1)
    go doit(i, wq, done, &wg)
  }

  fmt.Printf("doing work\n")
  for i := 0; i < worker_count; i++ {
    time.Sleep(time.Millisecond * time.Duration(100))
    wq <- fmt.Sprintf("worker: %d", i)
  }

  fmt.Printf("closing 'done' channel\n")
  close(done)
  fmt.Printf("block/wait until all workers are done\n")
  wg.Wait()
  fmt.Println("all done!")
}

func doit(worker_id int, wq <-chan interface{}, done <-chan struct{}, wg *sync.WaitGroup) {
  fmt.Printf("[%v] is working\n", worker_id)
  defer wg.Done()
  max_time := time.Second * time.Duration(5)
  for {
    select {
    case m := <-wq:
      fmt.Printf("[%v] m => %v\n", worker_id, m)
    case <-done:
      fmt.Printf("[%v] is done\n", worker_id)
      return
    case <-time.After(max_time):
      fmt.Printf("timeout > %s seconds!\n", max_time)
    }
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.