timestee
timestee

Reputation: 1096

do repetitive tasks at intervals in golang using time.AfterFunc, just a sample

I just want to do repetitive background tasks in Go, using time.AfterFunc,But seems something wrong with the logic. The out put just: interval call interval call

But at least 5 times to call the function if all things went normal.

package main

import (
    "fmt"
    "time"
    "os"
    "os/signal"
)

type Timer struct {
    Queue chan *TimeCall
}

func NewTimer(l int) *Timer {
    timer := new(Timer)
    timer.Queue = make(chan *TimeCall,l)
    return timer
}

type TimeCall struct {
    timer    *time.Timer
    callback func()
}

func (this *TimeCall) CallBack() {
    defer func() { recover() }()
    if this.callback != nil {
        this.callback()
    }
}

func (this *Timer) AfterFunc(d time.Duration, callback func()) *TimeCall {
    call := new(TimeCall)
    call.callback = callback
    call.timer = time.AfterFunc(d, func() {
        this.Queue <- call
    })
    return call
}



type PipeService struct {
    TimeCall *Timer
}

func (this *PipeService) AfterFunc(delay time.Duration, callback func()) *TimeCall {
    return this.TimeCall.AfterFunc(delay, callback)
}

func (this *PipeService) IntervalCall(interval time.Duration, callback func()) {
    this.TimeCall.AfterFunc(interval,func(){
        if callback != nil {
            callback()
        }
        this.AfterFunc(interval,callback)
    })
}

func (this *PipeService) Run(closeSig chan bool) {
    for {
        select {
        case <-closeSig:
            return
        case call := <-this.TimeCall.Queue:
            call.CallBack()
        }
    }
}

func main() {
    var closeChan chan bool
    InsPipeService := &PipeService{TimeCall: NewTimer(10)}
    InsPipeService.IntervalCall(2*time.Second,func(){
        fmt.Println("interval call")
    })

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, os.Kill)

    go func(){
        InsPipeService.Run(closeChan)
    }()

    time.Sleep(10*time.Second)
}

Run Code

Upvotes: 1

Views: 7740

Answers (2)

user6169399
user6169399

Reputation:

set time interval then call Start it will run user Job on each time intervals. set Enabled to false to stop it.
My Sample:

package main

import (
    "fmt"
    "sync"
    "time"
)

type IntervalTimer struct {
    Interval time.Duration
    Enabled  bool
    Job      func()
    Wg       sync.WaitGroup
}

func (it *IntervalTimer) isr() {
    if it.Enabled {
        it.Job()
        time.AfterFunc(it.Interval, it.isr)
    } else {
        it.Wg.Done()
    }
}

//trigger
func (it *IntervalTimer) Start() {
    if it.Enabled {
        it.Wg.Add(1)
        time.AfterFunc(it.Interval, it.isr)
    }
}

// user code:
var n int = 5
var it *IntervalTimer

func uerTask() {
    fmt.Println(n, time.Now()) // do user job ...
    n--
    if n == 0 {
        it.Enabled = false
    }
}
func main() {
    it = &IntervalTimer{Interval: 500 * time.Millisecond, Enabled: true, Job: uerTask}
    it.Start()

    //do some job ...
    it.Wg.Wait()
    fmt.Println("Bye")
}

Upvotes: 0

icza
icza

Reputation: 418535

time.AfterFunc() returns a *time.Timer, quoting form its doc:

The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc.

The time.Timer returned by time.AfterFunc() does not repeat, so what you see is perfectly normal: in your PipeService.IntervalCall() you execute the callback immediately, and it gets executed after the timeout.

Also note that you pass 2 as interval for the PipeService.IntervalCall() method. This interval parameter is of type time.Duraion. So when you pass 2, that won't be 2 seconds (but actually 2 nanoseconds). You should pass a value constructed from constants from the time package like:

InsPipeService.IntervalCall(2 * time.Second, func(){
    fmt.Println("interval call")
})

If you want repetition, use time.Ticker. For example the following code prints a message in every 2 seconds:

t := time.NewTicker(2 * time.Second)
for now := range t.C {
    fmt.Println("tick", now)
}

Or simply if you don't need the Ticker and you don't want to shut it down:

c := time.Tick(2 * time.Second)
for now := range c {
        fmt.Println("tick", now)
}

Upvotes: 6

Related Questions