Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money.
Runner Overall Design
// Package Runner manages the running and lifecycle of processes.
package runner
import (
"errors"
"os"
"os/signal"
"time"
)
//Runner runs a set of tasks for a given timeout period and can be shut down in case of an operating system outage.
type Runner struct {
// Interrupt channels transmit signals sent by the operating system
interrupt chan os.Signal
// The complete channel signals that the coroutine is complete
complete chan error
// timeout Will send a timeout signal
timeout <-chan time.Time
// Tasks contain a set of functions that are executed synchronously in indexed order.
tasks []func(int)
}
ErrTimeout is returned when a value is received on the timeout channel.
var ErrTimeout = errors.New("received timeout")
ErrInterrupt is returned when an event is received from the operating system.
var ErrInterrupt = errors.New("received interrupt")
// New returns a New Runner ready to be used.
func New(d time.Duration) *Runner {
return &Runner{
interrupt: make(chan os.Signal, 1),
complete: make(chan error),
timeout: time.After(d),
}
}
Copy the code
From the overall design, we can see that Runner has the following functions:
- The program can be terminated normally if the work is completed within the allotted timeout period;
- If the program doesn’t complete its work in time, it commits suicide;
- If the program receives an interrupt event from the operating system, it immediately tries to clean up the state and stop working.
Runner Detail Design
// Add tasks to Runner. A task is a function that takes a variable of type int. func (r *Runner) Add(tasks ... func(int)) { r.tasks = append(r.tasks, tasks...) } // The Start function runs all tasks and listens for channel events. Func (r *Runner) Start() error {// We want to receive all interrupt signals. Signal.notify (R.innterrupt, os.interrupt) // Running different tasks on different coroutines. Go func() {r.complete < -r.run ()}() select {// Signal when processing is complete. Case err := <-r.complete: return err // Sends a signal after a timeout case < -r.tymeout: return ErrTimeout}} // The run function executes each registered task. Func (r *Runner) run() error {for id, task := range r.asks {// Check the interrupt signal from the operating system. If r.gotinterrupt () {return ErrInterrupt} // Executes the registered task. Task (id)} return nil} // The gotInterrupt function verifies that an interrupt signal has been sent. Func (r *Runner) gotInterrupt() bool {select {// Signals when interrupt events are sent. Case <-r.interrupt: // Stop receiving any further signals. Signal.stop (R.i nterrupt) return true (Run as usual) Default: return false}}Copy the code
The above code is the task addition, as well as the task start, execute, interrupt steps, together constitute a complete life cycle.
For example
package main
import (
"log"
"os"
"time"
"github.com/goinaction/code/chapter7/patterns/runner"
)
// timeout specifies how many seconds the processing must be completed
const timeout = 3 * time.Second
// Main is the entry to the program
func main(a) {
log.Println("Starting work.")
// Assign a timeout for this execution
r := runner.New(timeout)
// Add tasks to be executed
r.Add(createTask(), createTask(), createTask(), createTask(), createTask(),createTask())
// Execute the task and process the result
iferr := r.Start(); err ! =nil {
log.Println(err.Error())
}
log.Println("Process ended.")}// createTask returns a base ID
// Hibernate a sample task for a specified number of seconds
// It is used to simulate work in progress
func createTask(a) func(int) {
return func(id int) {
log.Printf("Processor - Task #%d.", id)
time.Sleep(time.Duration(id) * time.Second)
}
}
Copy the code
The mock code simply sets up the task and shows what happens when the task succeeds and times out
2021/10/18 01:09:39 Starting work.
2021/10/18 01:09:39 Processor - Task #0.
2021/10/18 01:09:39 Processor - Task #1.
2021/10/18 01:09:40 Processor - Task #2.
2021/10/18 01:09:42 received timeout
2021/10/18 01:09:42 Process ended.
Copy the code