Go Language Practical Notes (15) | Go Concurrency Example-runner

Source: Internet
Author: User
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.

completeA 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.

completeis 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.

interruptis 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.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.