断路器设计模式睡眠与时间的关系。

I am trying to create a Circuit breaker pattern, I want to execute a command exec.Command and if it fails, retry in X defined amount of time, for testing purposes, I am doing something like this for testing time.AfterFunc:

package main

import (
    "fmt"
    "time"
)

func myFunc() error {
    for i := 1; i < 10; i++ {
        fmt.Printf("i = %+v
", i)
        if i%3 == 0 {
            return fmt.Errorf("error")
        }
    }
    return nil
}

func main() {

    run := make(chan struct{}, 1)

    run <- struct{}{}

    for {
        select {
        case <-run:
            err := myFunc()
            if err != nil {
                time.AfterFunc(3*time.Second, func() {
                    run <- struct{}{}
                })
            }
        default:
        }
    }
}

time.AfterFunc works for the above code, but not for the example below, I had to replace it with a sleep in order to achieve the expected results:

package main

import (
    "fmt"
    "os/exec"
    "time"
)

func Exec(done chan<- error) error {
    cmd := exec.Command("./start")
    if err := cmd.Start(); err != nil {
        return err
    }
    go func() {
        done <- cmd.Wait()
    }()
    return nil
}

func main() {
    var (
        run  = make(chan struct{}, 1)
        done = make(chan error, 1)
    )

    Exec(done)

    for {
        select {
        case <-run:
            err := Exec(done)
            if err != nil {
                fmt.Println(err)
                // time.AfterFunc(3*time.Second, func() {
                time.Sleep(3 * time.Second)
                run <- struct{}{}
            }
        default:
            select {
            case err := <-done:
                fmt.Println(err)
                run <- struct{}{}
            }
        }
    }
}

The content of ./sleep:

#!/bin/sh

sleep 3

And for testing, creating an error, I toggle perms:

chmod -x sleep
chmod +x sleep

Therefore wondering what are the differences between using time.AfterFunc and time.Sleep and what could be the best way of implementing this pattern.

Any time you hit the default case, the select ends immediately. In the top example, after you execute AfterFunc the for loop runs continually until run has items (after 3 seconds). Busy waits are usually bad. With the sleep solution, you never have a busy wait, which is good. I'm not sure I fully follow what you are trying to accomplish with the nested selects in the second example though.

Why do you need channels and asynchrony at all? Why not just:

retryCount := 0
for retryCount < 3 {
   err := doSomethingScary()
   if err == nil{
     //success! return results!
   } else{
     //failure! wait and retry
     time.Sleep(time.Second) //time.sleep is a good non-busy wait
   }
}
// max tries exceeded. Return error.