Golang game leaf series (six) time module cron expression

A, cron expression

Detailed expressions cron, cron written expression, cron expression examples

cron expression, expression that defines the execution time is mainly used to perform a frequency or timing of the job (task Timing) system. Cron expression is a string of 5 or 6 separated by a space, is divided into six or seven domains, each domain representing a meaning, Cron following two syntax:

Seconds Minutes Hours DayofMonth Month DayofWeek Year或 
Seconds Minutes Hours DayofMonth Month DayofWeek

From the right perspective, that is + in + May Day a few weeks every minute

1. Examples
  • 0 0 3 * * ? 每天3点执行
  • 0 5/10 3 * * ?, 5 minutes per day three points, 15 points, 25 points, 35 points, 45 points, 55 points performed several times, before / after the symbol indicates the start time, each symbol represents the incremented value.
  • 0 10 3 ? * 1 Weekly Sunday, 3:10 execution Note: 1 for Sunday, 2 for Monday ... 7 for Saturday
  • */5 * * * * ? Executed once every 5 seconds
  • 0 */1 * * * ? Executed once every minute
  • 0 0/3 * * * ? Trigger once every three minutes
  • 0 0/5 14 * * ? Trigger every 5 minutes during the day at 2:00 pm to 2:55 pm
  • 0 0 5-15 * * ? 5-15 Every day the whole point is triggered, - represents a range, such as using "10-12" in the hour field, it represents from 10 to 12, 10, 11, i.e.,
  • 0 0 10,14,16 * * ? Every day 10:00, 14:00, 4:00, comma is a list, such as using "1,2,4" in the week field, for Monday, Tuesday, Thursday
  • 0 10 3 ? * 1#3 The third week of each month, Sunday execution, # number can only appear position in week

Question mark can only appear in the date and location of these two weeks, represents the value of the uncertainty this position. Here is simple to write yourself * and? Understanding, give a simple example, if we specify 2 per month to perform the operation, then every month on the 2nd day of the week is uncertain, so the field can not be week *, * represents all the values, so use? That I do not care about No. 2 of the week, it is the love of the week is the week.

L ( "last") ( " last") "L" with the day-of-month field means "this month, last day"; with the day-of-week field, it simply means "7" or "SAT . " If and figures used in conjunction with the day-of-week field, it means "this month, the last day of the week" - for example:. "The last Friday of the month" "6L" means when we use the "L" when , do not specify a range or list of values is very important, otherwise, we will get some unexpected results. For example, 0 15 10 ? * 6L 2002-2005it represents 2002 to 2005 the last Friday of each month at 10:15 am triggered, 0 0 23 L * ?represents the last day of each month to perform a 23:00

二、https://github.com/robfig/cron
package main
//blog: xiaorui.cc
import (
    "github.com/robfig/cron"
    "log"
)
 
func main() {
    i := 0
    c := cron.New()
    spec := "0 */1 * * * *"
    c.AddFunc(spec, func() {
        i++
        log.Println("start", i)
    })
    c.Start()
    select{} //阻塞主线程不退出
 
}

AddFunc registered in the task to the scheduler, when the tasks to be performed will be used goroutines call, so that each task blocking does not occur. Note select which usage: select the golang functions and select, poll, epoll similar to that monitor IO operations, IO operation occurs when the trigger appropriate action.

More reference golang / robfig / cron library study notes

Three, leaf of AfterFunc

Cron Timer primarily a function of the timer services, is time.AfterFunc Timer package, is to facilitate the polymerization of the Skeleton.

Go language standard library provides support Timer:

func AfterFunc(d Duration, f func()) *Timer

After the f function call AfterFunc long wait time d, where the function f will be executed in another goroutine. Leaf offers a AfterFunc the same function, in contrast, f functions are executed in the call goroutine AfterFunc, thus avoiding the use of synchronization mechanisms:

skeleton.AfterFunc(5 * time.Second, func() {
    // ...
})

This feature is also through a channel to do.

type Dispatcher struct {
    ChanTimer chan *Timer
}

func NewDispatcher(l int) *Dispatcher {
    disp := new(Dispatcher)
    disp.ChanTimer = make(chan *Timer, l)
    return disp
}

// Timer
type Timer struct {
    t  *time.Timer
    cb func()
}

func (disp *Dispatcher) AfterFunc(d time.Duration, cb func()) *Timer {
    t := new(Timer)
    t.cb = cb
    t.t = time.AfterFunc(d, func() {
        disp.ChanTimer <- t
    })
    return t
}

Timer substantially constructed a type, comprising native and time.Timer callbacks due cb performed. Then, after time to native Timer, this configuration data is inserted into the channel disp.ChanTimer. In skeleton.Run years, it will automatically perform these Cb

// leaf\module\skeleton.go
func (s *Skeleton) Run(closeSig chan bool) {
    for {
        select {
        case <-closeSig:
            s.commandServer.Close()
            s.server.Close()
            for !s.g.Idle() || !s.client.Idle() {
                s.g.Close()
                s.client.Close()
            }
            return
        case ri := <-s.client.ChanAsynRet:
            s.client.Cb(ri)

        // 等待来自通道的数据
        case ci := <-s.server.ChanCall:
            s.server.Exec(ci)

        case ci := <-s.commandServer.ChanCall:
            s.commandServer.Exec(ci)
        case cb := <-s.g.ChanCb:
            s.g.Cb(cb)
        case t := <-s.dispatcher.ChanTimer:
            t.Cb()
        }
    }
}

In the official example_test.go is performed manually (<-d.ChanTimer).Cb(). And also demonstrates a call to Stop method clears a timer.

func ExampleTimer() {
    d := timer.NewDispatcher(10)

    // timer 1
    d.AfterFunc(1, func() {
        fmt.Println("My name is Leaf")
    })

    // timer 2
    t := d.AfterFunc(1, func() {
        fmt.Println("will not print")
    })
    t.Stop()

    // dispatch
    (<-d.ChanTimer).Cb()

    // Output:
    // My name is Leaf
}
Four, leaf of cron function

It appears that the library is done on the basis of github.com/robfig/cron on. Source follows

// Cron
type Cron struct {
    t *Timer
}

func (c *Cron) Stop() {
    if c.t != nil {
        c.t.Stop()
    }
}

func (disp *Dispatcher) CronFunc(cronExpr *CronExpr, _cb func()) *Cron {
    c := new(Cron)

    now := time.Now()
    nextTime := cronExpr.Next(now)
    if nextTime.IsZero() {
        return c
    }

    // callback
    var cb func()
    cb = func() {
        defer _cb()

        now := time.Now()
        nextTime := cronExpr.Next(now)
        if nextTime.IsZero() {
            return
        }
        c.t = disp.AfterFunc(nextTime.Sub(now), cb)
    }

    c.t = disp.AfterFunc(nextTime.Sub(now), cb)
    return c
}

Source code using nested countdown to call cb.

func ExampleCron() {
    d := timer.NewDispatcher(10)

    // cron expr
    cronExpr, err := timer.NewCronExpr("0/3 * * * * *")
    if err != nil {
        fmt.Println("cron expr error:",err)
        return
    }

    // cron
    var c *timer.Cron
    c = d.CronFunc(cronExpr, func() {
        fmt.Println("My name is Leaf")
        //c.Stop()
    })

    fmt.Println("begin cron",c)
    // dispatch
    (<-d.ChanTimer).Cb()
    select{
        case t := <-d.ChanTimer:
            t.Cb()
    }


    // Output:
    // My name is Leaf
}

Although I c.Stop to comment out, but did not like how I envisioned, like, once every 3 seconds to print My name is Leaf.

    cronExpr, err := timer.NewCronExpr("0/3 * * * * *")
    now := time.Now()
    nextTime := cronExpr.Next(now)
    if nextTime.IsZero() {
        fmt.Println("nextTime is Zero")
    }
    fmt.Println("sub time",nextTime.Sub(now))

This time it was not allowed to:

sub time 1.8513635s
V. Other
1. AND DELINQUENCY standard library to add a timer to sync it?

Do not use synchronization may cause problems, certain probability (typically may not happen), it may appear larger when the amount of users. You will find the Leaf's timer delay is because in some cases a timer to trigger the time, but now has a chance to perform, so it is waiting (may be considered to be the current goroutine busy to deal with other code), and the additional timer go off to a goroutine to perform, there is no need to wait (for a certain extent).

In fact, this problem is server frame rate control. The frame rate control is actually a dynamic, rather than a fixed time interval, you can look https://github.com/mangos/mangosd/blob/master/WorldRunnable.cpp#L78 implementation of and think about. Leaf in the best frame rate control should be implemented in the default select. More things we need to explore yourself

Reproduced in: https: //www.jianshu.com/p/b362e45e55a3

Guess you like

Origin blog.csdn.net/weixin_33881041/article/details/91076097