Golang:等待x时间再重新循环而不启动新的goroutine

I have a loop that needs to wait a random amount of time before looping again.

What I have:

for {
    rand.Seed(time.Now().UnixNano())
    r := rand.Int()
    //Do stuff
    t, _ := time.ParseDuration(string(r) + "ms")
    time.Sleep(t)
}

Unfortunately, the loops run multiple times, just like if time.Sleep was not working.

for loop doesn't spawn new goroutines unless you do it explicitly in cycle. By default next cycle starts only after previous is done. So you do not need any delay. Just make sure you do not use go keyword in your cycle.

If you want to launch goroutines:

for {
    rand.Seed(time.Now().UnixNano())
    r := rand.Int()
    //Do stuff in separate goroutine
    go DoStuff()
    t, _ := time.ParseDuration(string(r) + "ms")
    time.Sleep(t)
}

This way new goroutine will be launched.

You should check the error you are currently discarding from t, _ := time.ParseDuration: the time.Duration you are passing to Sleep is in its zero value, which causes the function to sleep for 0 nanoseconds.

Change #1: handle the error

    t, err := time.ParseDuration(string(r) + "ms")
    if err != nil {
        log.Println(err)
        return
    }

Resulting in:

time: invalid duration �ms

Since you are directly casting an integer to a string, you are messing with a whole lot of unicode logic you don't really want to.

Change #2: use strconv:

    t, err := time.ParseDuration(strconv.Itoa(r) + "ms")
    if err != nil {
        log.Println(err)
        return
    }

Result:

time: invalid duration 7936209096285811603ms

You get this error because time.Duration is represented using a int64 for storing nanoseconds. You are trying to pass an int representing MILLIseconds: you will be overflowing most of the time.

Change #3: directly assign to the Duration

for {
    rand.Seed(time.Now().UnixNano())
    r := rand.Int()
    log.Println(r)
    d := time.Duration(r)
    time.Sleep(d)
}

Now it works! The first integer generated by my function is 649508928394582529, which I am casting to time.Duration (again, this is an int64 of nanoseconds). This is approximatively 649508928 seconds: just over a week.

It looks like a lot. But keep in mind that you were trying to use the generated int as milliseconds: that is 10^6 times more.

Change #4: Seed outside the loop

While we are at it, let's polish a bit:

rand.Seed(time.Now().UnixNano())
for {
    r := rand.Int()
    log.Println(r)
    d := time.Duration(r)
    time.Sleep(d)
}

Explanation: a pseudorandom number generator is a state machine. You have to feed it a seed once, and then it will work forever. Giving it a different seed every time you run it is not improving its randomness: it's applying the same algorithm over and over on a predictable value (time). You can achieve the same exact degree of randomness by directly using time.Now()Unix()