I created a scheduler() function which will execute a passed function on an interval.
func scheduler(what func(), delay time.Duration) {
fmt.Printf("Starting scheduled process on interval %d\n", delay)
ticker := time.NewTicker(delay)
quit := make(chan bool, 1)
go func() {
for {
select {
case <- ticker.C:
what()
case <- quit:
ticker.Stop()
return
}
}
}()
<-quit
}
Scheduling the following ping function works perfectly.
func ping() {
fmt.Printf("Tick\n")
}
func main() {
scheduler(ping, time.Second)
}
However if I change ping to include an argument as so:
func ping(msg string) {
fmt.Printf ("%s\n", msg)
}
func main() {
scheduler(ping("Hello"), time.Second)
}
I get the compile error:
ping("Hi") used as value
How do I pass this function with arguments without passing it as the return value? I would like to keep the scheduler() function generic enough that I can use other functions that have different argument signatures.
Use an anonymous function which calls your underlying function:
func main() {
scheduler(func() {
ping("Hello")
}, time.Second)
}
This allows calling arbitrarily complex functions, or even using closures:
db := // connect to a DB
scheduler(func() {
ping(db, "Hello") // Or any other arbitrary complexity
}, time.Second)
Related
I'm trying to write a program in go that is similar to cron with the addition that jobs are given a max runtime and if a function exceeds this duration, the job should exit. Here is my my whole code:
package main
import (
"fmt"
"log"
"sync"
"time"
)
type Job struct {
ID string
MaxRuntime time.Duration
Frequency time.Duration
Function func()
}
func testFunc() {
log.Println("OPP11")
time.Sleep(7 * time.Second)
log.Println("OP222")
}
func New(ID, frequency, runtime string, implementation func()) Job {
r, err := time.ParseDuration(runtime)
if err != nil {
panic(err)
}
f, err := time.ParseDuration(frequency)
if err != nil {
panic(err)
}
j := Job{ID: ID, MaxRuntime: r, Frequency: f, Function: implementation}
log.Printf("Created job %#v with frequency %v and max runtime %v", ID, f, r)
return j
}
func (j Job) Run() {
for range time.Tick(j.Frequency) {
start := time.Now()
log.Printf("Job %#v executing...", j.ID)
done := make(chan int)
//quit := make(chan int)
//var wg sync.WaitGroup
//wg.Add(1)
go func() {
j.Function()
done <- 0
}()
select {
case <-done:
elapsed := time.Since(start)
log.Printf("Job %#v completed in %v \n", j.ID, elapsed)
case <-time.After(j.MaxRuntime):
log.Printf("Job %#v halted after %v", j.ID, j.MaxRuntime)
// here should exit the above goroutine
}
}
}
func main() {
// create a new job given its name, frequency, max runtime
// and the function it should run
testJob := New("my-first-job", "3s", "5s", func() {
testFunc()
})
testJob.Run()
}
What I'm trying to do is that in the second case in the select of the Run() function, it should exit the goroutine which is running the function. I tried to do this by wrapping the function in a for loop with a select statement which listens on a quit channel like this:
go func() {
for {
select {
case <-quit:
fmt.Println("quiting goroutine")
return
default:
j.Function()
done <- 0
}
}
}()
And then having quit <- 1 in the Run() function, but that doesnt seem to be doing anything. Is there a better of doing this?
As explained in the comments, the whole problem is that you want to cancel the execution of a function (j.Function) that isn't cancellable.
There's no way to "kill a goroutine". Goroutines work in a cooperative fashion. If you want to be able to "kill it", you need to ensure that the function running in that Goroutine has a mechanism for you to signal that it should stop what it's doing and return, letting the Goroutine that was running it finally terminate.
The standard way of indicating that a function is cancellable is by having it take a context.Context as its first param:
type Job struct {
// ...
Function func(context.Context)
}
Then you create the context and pass it to the j.Function. Since your cancellation logic is simply based on a timeout, there's no need to write all that select ... case <-time.After(...), as that is provided as built-in functionality with a context.Context:
func (j Job) Run() {
for range time.Tick(j.Frequency) {
go j.ExecuteOnce()
}
}
func (j Job) ExecuteOnce() {
log.Printf("Job %#v executing...", j.ID)
ctx, cancel := context.WithTimeout(context.Background(), j.MaxRuntime)
defer cancel()
j.Function(ctx)
}
Now, to finish, you have to rewrite the functions that you're going to be passing to your job scheduler so that they take context.Context and, very importantly, that they use it properly and cancel whatever they're doing when the context is cancelled.
This means that if you're writing the code for those funcs and they will somehow block, you'll be responsible for writing stuff like:
select {
case <-ctx.Done():
return ctx.Err()
case ...your blocking case...:
}
If your funcs are invoking 3rd party code, then that code needs to be aware of context and cancellation, and you'll need to pass down the ctx your funcs receive.
var funcTimer *time.Timer
func AddItem(){
defer funcTimer.Stop()
funcTimer = time.AfterFunc(1000*time.Millisecond, func() {
fmt.Println("Executed")
})
}
My goal is trying to make this function as a debounce, in a way that if it is called rapidly without a time delay, the func call will not run.
My problem here is funcTimer.Stop() does not seem to stop time.AfterFunc function
In javascript, the equivalent of this is by using ClearTimeout and SetTimeout.
Try https://github.com/bep/debounce:
the code is short enough to follow:
func New(after time.Duration) func(f func()) {
d := &debouncer{after: after}
return func(f func()) {
d.add(f)
}
}
type debouncer struct {
mu sync.Mutex
after time.Duration
timer *time.Timer
}
func (d *debouncer) add(f func()) {
d.mu.Lock()
defer d.mu.Unlock()
if d.timer != nil {
d.timer.Stop()
}
d.timer = time.AfterFunc(d.after, f)
}
to use:
debounces := New(100 * time.Millisecond)
debounces(func(){fmt.Println("a")})
debounces(func(){fmt.Println("b")})
debounces(func(){fmt.Println("c")}) // last one wins
https://play.golang.org/p/SOTB-VaGzWC
Let’s say you have a function that sets up your application server and returns a function that should be run right before your application exits.
func main() {
defer applicationExit()
}
func applicationExit() func() {
fmt.Println(" Application Setup Call")
return func() {
fmt.Println("Application Tear Down Call")
}
}
You will get the following output.
Output: Application Setup Call
What went wrong? Why don’t we see the output Application Tear Down Call
Your code does exactly what is expected:
func main() {
defer applicationExit()
}
Translated to English, this code means:
Wait until main exits, then call applicationExit().
This is what happens. But what you seem to want is, instead:
Execute applicationExit() immediately, and store the return value for later.
Wait until application exits, then call the anonymous function returned by applicationExit().
If we write this actual code, we get something like:
func main() {
cleanup := applicationExit() // Call applicationExit() immediately
defer cleanup() // Wait until main() exits, then call cleanup()
}
which can be shortened to:
func main() {
defer applicationExit()()
}
You have to invoke the function that is returned. applicationExit() just returns the function and does nothing else. To execute it you have to invoke the function.
func main() {
defer applicationExit()()
}
func applicationExit() func() {
fmt.Println(" Application Setup Call")
return func() {
fmt.Println("Application Tear Down Call")
}
}
Consider the following code:
type foo struct {
bar string
}
func f() *foo {
ret := &foo{"before"}
defer func() { ret.bar = "after" }()
return ret
}
func main() {
fmt.Println(f()) // prints "&{after}"
}
The motivation is having to return a struct but some of the fields need to be set only before returning (e.g. a timestamp of when the function completed).
Is deferring the field assignation a race condition?
Is it idiomatic?
Is there a better way?
The main benefit of using defer statement versus invoking something before returning from a function or routine is defer will run the statement even when a panic occurs before returning.
Thus it is commonly used to clean up resources (such as closing a file or network connection) rather than to set state.
The function below will not print or return "hello"
func f() string {
panic("omg")
fmt.Println("hello")
return "hello"
}
This code will print but won't return "hello"
func f() string {
defer fmt.Println("ello")
panic("omg")
return "hello"
}
To answer your question: No, it won't result in a race. Apart from the above difference, it is equivalent to calling something before the return statement.
You mention timestamping when a function completes. In that case you can use defer like this:
package main
import (
"fmt"
"time"
)
func main() {
foo()
}
func foo() {
defer trace("foo")()
time.Sleep(1 * time.Second)
}
func trace(fn string) func() {
start := time.Now()
return func() {
layout := "15:04:05.000"
end := time.Now()
fmt.Printf("%s start at %s, end at %s, total %s", fn, start.Format(layout), end.Format(layout), end.Sub(start))
}
}
Output: foo start at 23:00:00.000, end at 23:00:01.000, total 1s
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
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)
}
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")
}