How can I implement periodic task?

Hi all,

How can I implement periodic task?
I wrote this code:

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

extern crate time;

fn main() {

    let (tx, rx): (Sender<()>, Receiver<()>) = mpsc::channel();

    thread::spawn(move || {
        loop {
            tx.send(()).unwrap();
            thread::sleep_ms(1000);
        }
    });

    loop {
        rx.recv().unwrap();
        // some calculations...
        thread::sleep_ms(100);
        println!("{} cycle", time::now().strftime("%Y-%m-%d %H:%M:%S.%f").unwrap());
    }  
}

but it doesn't work very well...

Compiling periodic_task v0.1.0 (file:///C:/Users/T530/Documents/rust/periodic_task)
Running target\debug\periodic_task.exe
2015-07-22 14:56:13.457286100 cycle
2015-07-22 14:56:14.459906800 cycle
2015-07-22 14:56:15.460616500 cycle
2015-07-22 14:56:16.461079500 cycle
2015-07-22 14:56:17.462427600 cycle
2015-07-22 14:56:18.462931200 cycle
2015-07-22 14:56:19.464091800 cycle
2015-07-22 14:56:20.465049800 cycle
2015-07-22 14:56:21.465878000 cycle
2015-07-22 14:56:22.467183400 cycle
2015-07-22 14:56:23.467979600 cycle
2015-07-22 14:56:24.468914700 cycle
2015-07-22 14:56:25.469595200 cycle
2015-07-22 14:56:26.470242600 cycle
2015-07-22 14:56:27.471356200 cycle
2015-07-22 14:56:28.472256900 cycle
2015-07-22 14:56:29.473275800 cycle
2015-07-22 14:56:30.474510900 cycle
2015-07-22 14:56:31.475037500 cycle
2015-07-22 14:56:32.475623700 cycle
2015-07-22 14:56:33.476569900 cycle
2015-07-22 14:56:34.477139500 cycle
2015-07-22 14:56:35.477889100 cycle
2015-07-22 14:56:36.479194900 cycle
2015-07-22 14:56:37.480018300 cycle
2015-07-22 14:56:38.481529000 cycle
2015-07-22 14:56:39.482791700 cycle
2015-07-22 14:56:40.484008300 cycle
2015-07-22 14:56:41.484560200 cycle
2015-07-22 14:56:42.485621600 cycle
2015-07-22 14:56:43.486139100 cycle
2015-07-22 14:56:44.486933300 cycle
...

Is there any other way to implement periodic task?

[quote="kiug, post:1, topic:2190"]
but it doesn't work very well...
[/quote]Can you be more specific?

I would like to execute some code every 1 second,
in the above example, it doesn't work because a hundredth of a second is growing per cycle.

In other words, the cycle is getting longer.

You can't rely on something like thread::sleep_ms for that kind of accuracy. You need to look at the current time, compute when you want to "wake up", do your work, check the time again, ask the OS to sleep for an appropriate amount of time... and when you're woken up, work out the error between when you wanted to wake up and when you actually woke up, and adjust the next wake up time accordingly.

Also, the code you actually execute does add to the time needed for execution.

Probably it is the println!() here.

Edit: if you use thread::sleep_ms() your application stops executing and insdead gives control to the OS (which is scheduling all of your processes). If the OS thinks any other task is mor important than your program it could be that it accidentially waits more than a minute.

Also, if you need to use thread::sleep_ms() anywhere in your code, you have a problem with your applications design anyway, except in some very, very few cases.

If you need really high precision it would be better to use a Real Time Operating System, not Windows.

I am afraid I cannot help you any further as I don't know if there is a crate out there which let's you register a callback function which is then periodically called, what might be the best solution.

That is worded too strongly, IMO. There are quite a few situations where a thread can sit in the background, waking up periodically and doing some work, maybe checking files for updates, or doing some kind of "garbage collection" (not in the memory management sense).

I know. I just try to teach people to not use it if there is anything else they can do, without sacrificing performance of course.

I always try to utilize I/O so I can wait for something in the file to actually be written.
Also one can use channels if working with threads, or simply try to not leave any garbage in the first-place.

One use I could understand was sending _ping_s on some socket to keep the connection up/notify that you are still there, but out of that? Not really much.

Also it is okay to use in a W.I.P. but IMO there should be no, or at least only few well documented sleep()s in finished applications.

There is: https://crates.io/crates/schedule_recv
And it works:

extern crate time;
extern crate schedule_recv;

fn main() {

    let tick = schedule_recv::periodic_ms(1000);
    
    loop {
        tick.recv().unwrap();
        println!("{} cycle", time::now().strftime("%Y-%m-%d %H:%M:%S.%f").unwrap());
    }
}

C:\Users\T530\Documents\rust\periodic_task>cargo run
Compiling periodic_task v0.1.0 (file:///C:/Users/T530/Documents/rust/periodic_task)
Running target\debug\periodic_task.exe
2015-07-24 13:22:20.213996100 cycle
2015-07-24 13:22:21.215669000 cycle
2015-07-24 13:22:22.215747900 cycle
2015-07-24 13:22:23.214840400 cycle
2015-07-24 13:22:24.214742900 cycle
2015-07-24 13:22:25.214096300 cycle
2015-07-24 13:22:26.214553800 cycle
2015-07-24 13:22:27.214159100 cycle
2015-07-24 13:22:28.215745900 cycle
2015-07-24 13:22:29.214512800 cycle
2015-07-24 13:22:30.213921500 cycle
2015-07-24 13:22:31.214858600 cycle
2015-07-24 13:22:32.214420400 cycle
2015-07-24 13:22:33.214491500 cycle
2015-07-24 13:22:34.214799000 cycle
2015-07-24 13:22:35.214087300 cycle
2015-07-24 13:22:36.214793800 cycle
2015-07-24 13:22:37.214199400 cycle
2015-07-24 13:22:38.215569500 cycle
2015-07-24 13:22:39.215164500 cycle
...

1 Like

I want to use this pattern to read PLC registers (using Modbus protocol).

It works, however it is not what I said.

I thought of registering a callback function like this:

extern crate cool_crate;
use cool_crate::register_callback;

fn my_foo()
{
    println!("Meow");
}

fn main()
{
    let handle=register_callback(1000,my_foo);
}

Maybe with closures too, but I don't think that is possible:

fn main()
{
    let handle=register_callback(1000,move||
    {
        println!("Meow");
    });
}

Still, thanks for sharing. That crate can be damn useful!

There's also this library:

https://crates.io/crates/snooze-rs

which I've found to be a little more accurate than schedule_recv (but just as precise), at least on linux. It really just wraps clock_nanosleep.