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