lotusirous / go-concurrency-patterns

Concurrency patterns in Go

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

timeout example is unsuitable

kele1997 opened this issue · comments

// timeout for the whole conversation
timeout := time.After(5 * time.Second)
for {
select {
case s := <-c:
fmt.Println(s)
case <-timeout:
fmt.Println("You talk too much.")
return
}
}

We always think timeout as the time that one service has on response. In this case, timeout 's meaning should be ** we don't receive any message from c channel for 5 seconds.

so maybe the right code is :

timeout := time.After(5 * time.Second)
for {
       select {
       case s := <-c:
               fmt.Println(s)
               timeout = time.After(5 * time.Second) // update timeout time
       case <-timeout:
               fmt.Println("channel has no response for 5 seconds")
               return
       }
}

the origin code seems that you want to receive from every channel for same time, like linux cfs scheduler. so maybe in that case we should use ticker

package main

import (
        "fmt"
        "time"
)

func boring(id int) <-chan string {
        c := make(chan string)
        go func() {
                 c <- "" // skip the first time
                for i := 0; ; i++ {
                        c <- fmt.Sprintf("%d, %s", id, time.Now().Format("2006-01-02 15:04:05"))
                        time.Sleep(1 * time.Second)
                }
        }()
        return c
}

func main() {
        timeout := time.NewTicker(5 * time.Second)
        c1 := boring(1)
        c2 := boring(2)
        jobchannels := []<-chan string{c1, c2}
        i := 0
        for {
                select {
                case s := <-jobchannels[i]:
                        fmt.Println(s)
                case <-timeout.C:
                        fmt.Printf("%d has talk for 5 secs\n", i+1)
                        i = (i + 1) % len(jobchannels)
                }
        }
}

and it's output is


1, 2021-03-17 11:27:03
1, 2021-03-17 11:27:04
1, 2021-03-17 11:27:05
1, 2021-03-17 11:27:06
1, 2021-03-17 11:27:07
1 has talk for 5 secs

2, 2021-03-17 11:27:08
2, 2021-03-17 11:27:09
2, 2021-03-17 11:27:10
2, 2021-03-17 11:27:11
2, 2021-03-17 11:27:12
2 has talk for 5 secs
1, 2021-03-17 11:27:08
1, 2021-03-17 11:27:14
1, 2021-03-17 11:27:15
1, 2021-03-17 11:27:16
....
commented

I think this example does what it intends to, which is to run for a duration of timeout from the start , as the select is wrapped in an infinite loop.
If the intention is to give an example of waiting for a fixed timeout for each request(such as get a string from boring goroutine), he will write

for {
       select {
       case s := <-c:
               fmt.Println(s)
       case <-time.After(5 * time.Second):
               fmt.Println("channel has no response for 5 seconds, you talk too slow")
               return
       }
}

which does refresh the timer in each select.
inferred from the example code fmt.Println("You talk too much.") , this is more likely to run for a fixed period of time and
get a bunch of responses before quit, but not an example of timeout for each single request, cause that would happen at the first request, which make the talk too much part nonsense