执行:不阻止操作时根据信号采取行动

I want to fire the ps command continuously in a goroutine to monitor mem and cpu usages. I didn't use top because top doesn't allow me to select columns as ps does. This goroutine needs to receive a stop signal to stop the command, but I don't know how not to block running the command while waiting the signal. For top I can do:

top := exec.Command("top")
<-stop // blocking
top.Process.Signal(os.Kill)

But for ps if I do:

ps := exec.Command("ps")
for {
    ps.Run()
    <-stop
}

The above code will block on stop. I want to keep firing ps.Run(), while being able to stop when a stop signal is ready. Thanks.

One way that you could achieve this is by utilizing the for/select timeout idiom, there are a couple of similar methods of doing this. Take this trivial example:

package main

import (
    "fmt"
    "time"
)

func main() {
    abort := make(chan struct{})

    go func() {
        for {
            select {
            case <-abort:
                return
            case <-time.After(1 * time.Second):
                // replace fmt.Println() with the command you wish to run
                fmt.Println("tick")
            }
        }
    }()
    // replace time.Sleep() with code waiting for 'abort' command input
    time.Sleep(10 * time.Second)
    abort <- struct{}{}
}

To modify this example to work in your circumstance place the code that you want to run in the <-time.After(): case, which (in this instance) will run once per second, if no other case is available to receive for that duration. And instead of time.Sleep() which I placed at the end, put the code that will interrupt the <-time.After(): case, sending <- struct{}{} on the abort channel (or whatever you name it).

NOTE: In an earlier version of this answer I had abort as a chan bool, as I like the clarity of <-abort true and don't consider chan struct{} to be as clear, I opted to change it in this example however, as <- struct{}{} isn't unclear, especially once you've gotten used to the pattern.

Also, if you want the command to execute on each iteration of the for loop and not wait for a timeout then you can make that case default:, remove <-time.After() and it will run on each iteration of the loop where another channel is not ready to receive.

You can play with this example in the playground if you'd like, although it will not allow syscalls, or the default: case example to be run in that environment.

To run it over and over, without sleep:

func run(stop <-chan struct{}) {
    ps := exec.Command("ps")
    for {
        select {
        case <-stop:
            return
        default:
            ps.Run()
            //processing the output
        }
    }
}

And then go run(stop). And to run it every 3 seconds (for example):

func run(stop <-chan struct{}) {
    ps := exec.Command("ps")
    tk := time.Tick(time.Second * 3)
    for {
        select {
        case <-stop:
            return
        case <-tk:
            ps.Run()
            //processing the output
        }
    }
}