cron
cron copied to clipboard
once task
Could you run the task once?
No, but your task could remove itself from Cron when it starts running.
This is my way of implementing a one-time task.
https://github.com/93Alliance/lodago/blob/master/crontab.go
you can use it in this way
c := lodago.NewCrontab()
go c.Start()
defer c.Stop()
t := lodago.CronTime{
lodago.Once,
"2020",
"5",
"8",
"14",
"19",
"",
"",
}
job1 := func() {
fmt.Println("一次性任务")
}
c.AddJob(&t, job1)
for {
time.Sleep(time.Duration(1 * time.Second))
fmt.Println(c.GetEntries())
}
I hava same problem. And I had implemented it.
I hava same problem. And I had implemented it.
How did you do it?
@93Alliance #317 Here
How about using sync.Once?
For example you could do something like:
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"sync"
"time"
)
func main() {
kron := cron.New()
once := sync.Once{}
_, _ = kron.AddFunc("* * * * * ", func() {
once.Do(func() {
fmt.Println("Only prints once")
})
})
kron.Start()
defer kron.Stop()
time.Sleep(time.Duration(5 * time.Minute))
}
Note that the instance of sync.Once{} needs to be created outside of the function passed to cron.AddFunc(). With this implementation the event/job is still firing but the code invoked only runs once.
@buildscientist It is right. But the function will be called repeatedly. This is my code.
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"log"
"os"
"sync"
)
func main() {
logger := log.New(os.Stdout, "DEBUG", log.Ldate|log.Ltime)
wg := make(chan int64)
kron := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(logger)))
once := sync.Once{}
_, _ = kron.AddFunc("* * * * * ", func() {
once.Do(func() {
fmt.Println("Only prints once")
})
})
kron.Start()
defer kron.Stop()
<-wg
}
And this is my logs:
DEBUG2020/06/04 15:25:07 start DEBUG2020/06/04 15:25:07 schedule, now=2020-06-04T15:25:07+08:00, entry=1, next=2020-06-04T15:26:00+08:00 DEBUG2020/06/04 15:26:00 wake, now=2020-06-04T15:26:00+08:00 Only prints once DEBUG2020/06/04 15:26:00 run, now=2020-06-04T15:26:00+08:00, entry=1, next=2020-06-04T15:27:00+08:00 DEBUG2020/06/04 15:27:00 wake, now=2020-06-04T15:27:00+08:00 DEBUG2020/06/04 15:27:00 run, now=2020-06-04T15:27:00+08:00, entry=1, next=2020-06-04T15:28:00+08:00 DEBUG2020/06/04 15:28:00 wake, now=2020-06-04T15:28:00+08:00 DEBUG2020/06/04 15:28:00 run, now=2020-06-04T15:28:00+08:00, entry=1, next=2020-06-04T15:29:00+08:00 DEBUG2020/06/04 15:29:00 wake, now=2020-06-04T15:29:00+08:00 DEBUG2020/06/04 15:29:00 run, now=2020-06-04T15:29:00+08:00, entry=1, next=2020-06-04T15:30:00+08:00 DEBUG2020/06/04 15:30:00 wake, now=2020-06-04T15:30:00+08:00 DEBUG2020/06/04 15:30:00 run, now=2020-06-04T15:30:00+08:00, entry=1, next=2020-06-04T15:31:00+08:00 DEBUG2020/06/04 15:31:00 wake, now=2020-06-04T15:31:00+08:00 DEBUG2020/06/04 15:31:00 run, now=2020-06-04T15:31:00+08:00, entry=1, next=2020-06-04T15:32:00+08:00 DEBUG2020/06/04 15:32:00 wake, now=2020-06-04T15:32:00+08:00 DEBUG2020/06/04 15:32:00 run, now=2020-06-04T15:32:00+08:00, entry=1, next=2020-06-04T15:33:00+08:00 DEBUG2020/06/04 15:33:00 wake, now=2020-06-04T15:33:00+08:00 DEBUG2020/06/04 15:33:00 run, now=2020-06-04T15:33:00+08:00, entry=1, next=2020-06-04T15:34:00+08:00 DEBUG2020/06/04 15:34:00 wake, now=2020-06-04T15:34:00+08:00 DEBUG2020/06/04 15:34:00 run, now=2020-06-04T15:34:00+08:00, entry=1, next=2020-06-04T15:35:00+08:00
@Jinnrry
That is correct the function passed to the job does get called repeatedly but because it is wrapped in in sync.Once it'll only actually be called once.
As @robfig mentioned once the job has run you can remove it from the crontab.
Why not? You can just implement Once schedule by yourself. Something like that should work:
package main
import (
"github.com/robfig/cron/v3"
)
type Task func()
func (t Task) Run() {
t()
}
type OnceSchedule struct {
called bool
}
func (o *OnceSchedule) Next(_ time.Time) time.Time {
if o.called {
return time.Time{}
}
o.called = true
return time.Now()
}
func foo() {
// do something...
}
func main() {
kron := cron.New()
kron.Schedule(new(OnceSchedule), Task(foo))
kron.Start()
defer kron.Stop()
}
@small-egg Not a bad idea but your implementation isn't safe for concurrency. I would add a mutex to your OnceSchedule struct to insure you don't have 2 goroutines executing the job at the same time.
@buildscientist thank you for your reply, but it's quite safe because setting of a bool variable is exactly one instruction of any processor, and there is the only one state switching to true.
@small-egg In your example use case - yes,but what happens if I have multiple instances of kron doing different tasks followed by a task that should only be done once. This is a very common pattern where I have multiple jobs scheduled to do specified tasks with at least one job doing a certain task.
For example - I have 2 scheduled jobs each with their own instance returned by cron.New(). Both jobs do different tasks but only one should do Task(foo).
In this scenario you could have both jobs that complete at the same time (on a multi-core box) and attempt to run Task(foo) at the same time leading to a potential race condition.
I realize my example might seem "convoluted" but this is a library which could potentially be used in the manner I just described.
Also note that the documentation clearly states:
Since the Cron service runs concurrently with the calling code, some amount of care must be taken to ensure proper synchronization.
All cron methods are designed to be correctly synchronized as long as the caller ensures that invocations have a clear happens-before ordering between them.
@robfig any thoughts on this :)
@small-egg In your example use case - yes,but what happens if I have multiple instances of kron doing different tasks followed by a task that should only be done once. This is a very common pattern where I have multiple jobs scheduled to do specified tasks with at least one job doing a certain task.
For example - I have 2 scheduled jobs each with their own instance returned by
cron.New(). Both jobs do different tasks but only one should doTask(foo).In this scenario you could have both jobs that complete at the same time (on a multi-core box) and attempt to run
Task(foo)at the same time leading to a potential race condition.I realize my example might seem "convoluted" but this is a library which could potentially be used in the manner I just described.
Also note that the documentation clearly states:
Since the Cron service runs concurrently with the calling code, some amount of care must be taken to ensure proper synchronization. All cron methods are designed to be correctly synchronized as long as the caller ensures that invocations have a clear happens-before ordering between them.
@robfig any thoughts on this :)
The library provides several common kinds of schedule implementation. If you want any other implementation, you can do it yourself due to Schedule interface implementation of that you should pass to the cron.(*Cron).Schedule method. Your example is really unusual, so it's not surprising, that the library doesn't have the support of it out of the box.
In your case, you can implement a schedule with an exclusive lock, and pass it to both krons to prevent multiple jobs from launching at the same time.
No, but your task could remove itself from Cron when it starts running.
To remove itself, how to get itself entryId in the running job?