This is a creation in Article, where the information may have evolved or changed.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
This article demonstrates the use of channels to monitor the execution time, life cycle, and even termination of a program, through an example. Our program is called runner, we can call 执行者
it, it can perform any task in the background, and we can also control the performer, such as forcing the termination of it.
Now let's start by using the knowledge of our previous more than 10 series to build our runner and use a struct type.
123456789 |
//a performer who can perform any task, but these tasks are restricted, //the performer can terminate it by sending a termination signal type Runner struct {tasks [] func (int //Tasks to perform complete chan Error //For notification task complete timeout <-chan time . time //How long are these tasks completed interrupt chan os . signal //can control signal of forced termination} |
example, we define a struct type Runner
, Runner
which contains what tasks to perform tasks
, and then uses the complete
notification task to complete, but the performer has a time limit, which is timeout
, if not done within the limited time, You will receive a notification of the timeout and will receive a notification of completion if it is completed. Note that this timeout
is a one-way channel and can only be received.
complete
A channel defined as a type, which is the reason for returning an error if there is a problem with the execution of the error
task, or if no error occurs nil
.
In addition, we define an interrupt signal so that we can terminate the performer at any time.
With the struct, we then define a factory function New
that returns what we need Runner
.
1234567 |
func New (tm time. Duration) *Runner {return &runner{complete:make (chan error), Timeout:time. After (tm), interrupt:make (chan os. Signal,1),}} |
This New
function is very concise, it can help us to initialize a very quickly Runnner
, it has only one parameter, to set the time-out for this performer. This timeout interval is passed on to the time.After
function, which can be tm
followed by a time.Time
type of only one-way channel to be received by the accomplice, to tell us that it is time.
complete
is a unbuffered channel, that is, a synchronous channel, because we want to use it to control whether our entire program terminates, so it must be a synchronous channel, to let main routine wait, the same task is completed or forced to terminate.
interrupt
is a buffered channel, because we can receive at least one operating system interrupt information so that the go runtime will not be blocked when sending this signal, if the unbuffered channel will be blocked.
What does the system signal mean, for example, when we press in the execution of the program, Ctrl + C
this is an interrupt signal that tells the program to be forced to terminate.
Here we initialize the three fields of the struct, and the executed task is tasks
not initialized, the default is 0 value nil
, because it is a slice. But our executor can not Runner
have no task, since Runner
the initialization time is not, then we define a method, through the method to add to the performer needs to perform the task.
1234 |
//will need to perform the task, add to the runner func(R *runner)Add(Tasks ... func (int)) Append (R.tasks,tasks ...)} |
This is not much to be explained, it r.tasks
is a slice, to store the tasks that need to be performed. You can add tasks by using the built-in append
functions. With the use of variable parameters, it is convenient to add one or more tasks flexibly.
Here we need the performer, how to Runner
Add the task, how to get a performer, all have, the following is how to start the performer how to run the task? How do I force a task to be interrupted at run time? Before we do this, we'll define two of our two error variables for use in the next code instance.
12 |
var errtimeout = errors. New ("Performer Execution timeout") var errinterrupt = errors. New ("performer interrupted") |
Two types of errors, one representing a time-out error, and one representing the error because it was interrupted. Let's take a look at how to perform a task.
12345678910111213141516171819202122 |
//Perform a task that returns an interrupt error when the interrupt signal is received during execution//If the task is fully executed and no interrupt signal has been received, nil is returned func (R *runner) run() error { forID, Task: =RangeR.tasks {ifR.isinterrupt () {returnErrinterrupt}task (ID)}return Nil}//Check if the interrupt signal is received func (R *runner) isinterrupt() bool {Select{ Case<-r.interrupt:signal. Stop (R.interrupt)return truedefault:return false}} |
The new run
method is also very simple, will use for loop, run the task continuously, before each task run, will detect whether the interrupt signal received, if not received, then continue to execute, until the completion, return nil, if the interrupt signal is received, then directly return the interrupt error type, Task execution terminated.
Notice here that the isInterrupt
function, when implemented, uses multiplexing based on, select
and much select
switch
like it, except that each case
is a communication operation. So which case
block do you choose? The principle is which case
of the communication operations can be executed on which execution, if there are more than one can execute at the same time case
, then randomly select an execution.
For our method, if r.interrupt
the value is not accepted, the statement block will be executed, default
return false
, once r.interrupt
the value can be received, the go runtime will be notified to stop receiving the interrupt signal, and then return true
.
If this is not the default
case, it select
will block until the r.interrupt
value can be received, because the logic requirement in our example cannot be blocked, so we used it default
.
Well, the basic work is done, and now we're going to do all of our tasks and monitor the completion of the task at all times, executing the event timeout.
12345678910111213141516 |
//start performing All tasks and monitor channel events func start () error {//what system signals you want to receive signal. Notify (R.interrupt, OS. Interrupt) go func () {r.complete <-r.run ()} () select {case err: = <-r.complete:return errcase <-r.timeout:return errtimeout}} |
signal.Notify(r.interrupt, os.Interrupt)
, this indicates that if there is a signal that the system is interrupted, it can be sent r.interrupt
.
The execution of the task, where a groutine is opened, and then the run
method is called, and the result is sent to the channel r.complete
. The last is to use a multiplexed select
, which channel can be manipulated, which is returned.
At this point, there are only two cases, either the task is completed or the time is up and the task execution times out. From the code in front of us, the task is completed in two different cases, one is not finished, but received an interrupt signal, interrupted, the return of the interrupt error, one is successful completion, the return of nil.
Now summarize the code, it's easy to get a unified understanding, all the code is as follows
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 666768697071727374 |
PackageCommonImport("Errors""OS""Os/signal""Time")varErrtimeout = errors. New ("Performer Execution Timeout")varErrinterrupt = errors. New ("performer is interrupted")//A performer that can perform any task, but these tasks are limited to completion,//The performer can terminate it by sending a termination signaltypeRunnerstruct{Tasks [] func(int) //task to perform Complete Chan ErrorUsed to notify all tasks to completeTimeout<-Chan Time. TimeHow long are these tasks completed?Interrupt Chan OS.SignalCan control the signal of forced termination}func New(tm time. Duration)*Runner{return&runner{complete: Make(ChanError), Timeout:time. After (tm), interrupt: Make(ChanOs. Signal,1),}}//will need to perform the task, add to the runner func (R *runner) Add(Tasks ... func (int)){r.tasks =Append(R.tasks, Tasks ...)}//Perform a task that returns an interrupt error when the interrupt signal is received during execution//If the task is fully executed and no interrupt signal has been received, nil is returned func (R *runner) run() error { forID, Task: =RangeR.tasks {ifR.isinterrupt () {returnErrinterrupt}task (ID)}return Nil}//Check if the interrupt signal is received func (R *runner) isinterrupt() bool {Select{ Case<-r.interrupt:signal. Stop (R.interrupt)return truedefault:return false}}//start to perform all tasks and monitor channel events func (R *runner) Start() error {//What system signals you want to receiveSignal. Notify (R.interrupt, OS. Interrupt)Go func() {r.complete <-r.run ()} ()Select{ CaseERR: = <-r.complete:returnErr Case<-r.timeout:returnErrtimeout}} |
common
Runner
We have finished developing this bag, so let's write an example to try it out.
123456789101112131415161718192021222324252627282930313233343536 |
PackageMainImport("Flysnow.org/hello/common""Log""Time""OS") func main() {log. Println ("... Start to perform the task ... ") Timeout: =3* Time. Secondr: = Common. New (timeout) r.add (CreateTask (), CreateTask (), CreateTask ())ifErr:=r.start (); err!=Nil{SwitchErr { CaseCommon. Errtimeout:log. PRINTLN (ERR) OS. Exit (1) CaseCommon. Errinterrupt:log. PRINTLN (ERR) OS. Exit (2)}}log. Println ("... End of task execution ... ")} func createtask() func(int) {return func(id int) {log. Printf ("Executing task%d", id) time. Sleep (time. Duration (ID) * time. Second)}} |
The example is very simple, defining a task timeout of 3 seconds, adding 3 generated tasks, each of which prints one of the tasks being performed and then sleeps for a period of time.
Call to r.Start()
start the task, if everything is OK, return nil, and then print out ...任务执行结束...
, but in our example, because of the time-out and the task setting, the result is the execution timeout.
12345 |
2017/04/15 22:17:55 ... Start to perform the task ... 2017/04/15 22:17:55 Executing a task 02017/04/15 22:17:55 executing a task 12017/04/15 22:17:56 executing a task 22017/04/15 22:17:58 performer Execution timeout |
If we change the time-out to 4 seconds or more, we print ...任务执行结束...
. Here we can also test another system interrupt situation, after running the program in the terminal, quickly and continuously press Ctrl + C
, you can see 执行者被中断
the printout information.
Here, this article has to be completed, in this example, we demonstrate the use of channel communication, synchronous waiting, monitoring procedures and so on.
In addition, this performer is also a very good model, such as we write, to the scheduled task to execute, such as cron, this mode we can also expand, more efficient concurrency, more flexible control program life cycle, more efficient monitoring and so on, this everyone can try, You can change it based on your needs.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.