Example implementation of Golang Cron timed task

  • 2020-10-31 21:46:51
  • OfStack

Let me get straight to 1


package main

import (
  "fmt"
  "github.com/robfig/cron"
  "log"
  "strings"
  "time"
)

func CronTask() {
  log.Println("******** ******* *******")
}

func CronTest() {
  log.Println("Starting Cron...")

  c := cron.New()
  c.AddFunc("* * * * * *", CronTask) //2 * * * * *, 2  The end of the minute 2s perform 1 time 
  c.Start()

  t1 := time.NewTimer(time.Second * 10) // ?time.Second * 10  What you mean?  *100 All right? 
  for {
    select {
    case <-t1.C:
      fmt.Println("Time now:", time.Now().Format("2006-01-02 15:04:05")) //  Why set a specific time 
      t1.Reset(time.Second * 10)
    }
  }
}

func main() {
  fmt.Println(strings.Repeat("START ", 15))
  CronTest()
  fmt.Println(strings.Repeat("END ", 15))
}

The core timer code is just 3 lines long


c := cron.New()
c.AddFunc("* * * * * *", CronTask)
c.Start()

What does that last code do?

1 When I first saw the sample code, I had a puzzle, such as comments in the code


t1 := time.NewTimer(time.Second * 10)

Here time.Second*10 what is it? Could I write *100? I changed it so I could, but I'm even more confused if I didn't have to do it why would I have to do it?

And then there's for-ES18en-ES19en, which is also a 1 in a mud-headed state

Run the code, derive the principle from the result, execute the result once

[

START START START START START START START START START START START START START START START
2020/05/01 07:38:07 Starting Cron...
2020/05/01 07:38:08 ******** ******* *******
2020/05/01 07:38:09 ******** ******* *******
2020/05/01 07:38:10 ******** ******* *******
2020/05/01 07:38:11 ******** ******* *******
2020/05/01 07:38:12 ******** ******* *******
2020/05/01 07:38:13 ******** ******* *******
2020/05/01 07:38:14 ******** ******* *******
2020/05/01 07:38:15 ******** ******* *******
2020/05/01 07:38:16 ******** ******* *******
2020/05/01 07:38:17 ******** ******* *******
Time now: 2020-05-01 07:38:17
2020/05/01 07:38:18 ******** ******* *******
2020/05/01 07:38:19 ******** ******* *******
2020/05/01 07:38:20 ******** ******* *******
2020/05/01 07:38:21 ******** ******* *******
2020/05/01 07:38:22 ******** ******* *******
2020/05/01 07:38:23 ******** ******* *******
2020/05/01 07:38:24 ******** ******* *******
2020/05/01 07:38:25 ******** ******* *******
2020/05/01 07:38:26 ******** ******* *******
2020/05/01 07:38:27 ******** ******* *******
Time now: 2020-05-01 07:38:27
2020/05/01 07:38:28 ******** ******* *******

]

These are the running snippets, and there are two big findings

There are START START START... No END END END... : indicates that the code is blocked in the timer while executing. The timer has not finished executing and will never execute END The spacing is exactly 10s

Oh, so time.NewTimer is a timer, and when that interval is over, it reopens one. The purpose of the block for-ES93en-ES94en is to block the process and not allow the program to end. Understand right?

If so, remove ES97en-ES98en-ES99en you can also stop 10s when you run the first timer, right? Under test: block for-ES102en-ES103en, output

[

START START START START START START START START START START START START START START START
2020/05/01 07:56:22 Starting Cron...
END END END END END END END END END END END END END END END

]

It seems that blocking is mainly achieved by for-ES117en-ES118en. What is the principle?

Remove t1. How does Reset work?


t1 := time.NewTimer(time.Second * 10) // ?time.Second * 10  What you mean?  *100 All right? 
  for {
    fmt.Println("hihihihi")
    select {
    case <-t1.C:
      fmt.Println("hello")
    }
  }

The output

[

START START START START START START START START START START START START START START START
2020/05/01 08:12:21 Starting Cron...
hihihihi
2020/05/01 08:12:22 ******** ******* *******
2020/05/01 08:12:23 ******** ******* *******
2020/05/01 08:12:24 ******** ******* *******
2020/05/01 08:12:25 ******** ******* *******
2020/05/01 08:12:26 ******** ******* *******
2020/05/01 08:12:27 ******** ******* *******
2020/05/01 08:12:28 ******** ******* *******
2020/05/01 08:12:29 ******** ******* *******
2020/05/01 08:12:30 ******** ******* *******
2020/05/01 08:12:31 ******** ******* *******
hello
hihihihi
2020/05/01 08:12:32 ******** ******* *******
2020/05/01 08:12:33 ******** ******* *******
2020/05/01 08:12:34 ******** ******* *******
2020/05/01 08:12:35 ******** ******* *******
2020/05/01 08:12:36 ******** ******* *******

]

To make matters worse, reset was removed and the first timer 10s was run. Instead of listening, it was executed without stopping

The print in the for cycle is not a big chunk of the case, but a hit of the case, so it's time to understand how select-ES180en works

select case

And the convention is to do the last example


package main

import (
  "fmt"
  "strings"
)

func SelectTest() {
  intChan := make(chan int, 1)
  stringChan := make(chan string, 1)
  intChan <- 123456
  stringChan <- "hello"

  select {
  case value := <-intChan:
    fmt.Println(value)
  case value := <- stringChan:
    fmt.Println(value)
  }
}

func main() {
  fmt.Println(strings.Repeat("START ", 15))
  SelectTest()
  fmt.Println(strings.Repeat("END ", 15))
}

After many executions, you can see that the output is 123456, "hello" is variable

select grammar

Each case must be a communication

If 1 communication can be made it is executed and the others are ignored

If there are multiple case executables, one will be selected at random

If there is no case, if there is default, execute the default statement; Otherwise, it will block until some communication is feasible

There are still a lot of problems here. Make sure you understand the select statement in section 1

Go back to the timer task from 1

Review topic, timing principle analysis


t1 := time.NewTimer(time.Second * 10) 
  for {
    select {
    case <-t1.C:
      fmt.Println("Time now:", time.Now().Format("2006-01-02 15:04:05"))
      t1.Reset(time.Second * 10)
    }
  }
1 timer t1 is generated and for loop is executed. Before the start time of 1 (10s) arrives and the task is not blocked, it will block in case. When the timing time is up, execute the statement in case; And then it goes back to the timing Go back to the for loop and repeat the above story

In the above code, an attempt to remove ES226en1.Reset from case resulted in a different execution of the timed task. The reason was that after the statement in case was executed, the for loop was executed. Since there was no new communication (the case statement could never be satisfied), and there was no default statement, it could block again.

Compared with case t1 Reset, t1 Reset is more flexible, because it can do one more flexible operation each time it satisfies case, such as jumping out of the loop, doing one more statistical printing, etc.


Related articles: