6

I'm looking for a timer which uses threads, not plain time.sleep:

from threading import Timer

def x():
    print "hello"
    t = Timer(2.0, x)
    t.start()

t = Timer(2.0, x)
t.start()
Veedrac
  • 58,273
  • 15
  • 112
  • 169
Tkinter2
  • 73
  • 1
  • 4

2 Answers2

7

You can use the timer crate

extern crate timer;
extern crate chrono;

use timer::Timer;
use chrono::Duration;
use std::thread;

fn x() {
    println!("hello");
}

fn main() {
    let timer = Timer::new();
    let guard = timer.schedule_repeating(Duration::seconds(2), x);
    // give some time so we can see hello printed
    // you can execute any code here
    thread::sleep(::std::time::Duration::new(10, 0));
    // stop repeating
    drop(guard);
}
malbarbo
  • 10,717
  • 1
  • 42
  • 57
  • Could you use this to update a field in a mutable struct? – lsund Sep 25 '16 at 16:31
  • @lsund If you mean whether or not it's possible to leave a mutable reference in the closure accessible from elsewhere, from the crate's source it looks like the answer is 'no' barring a `Mutex`'d or `Arc`'d etc. ref to a `Cell`-and-friends global (or `unsafe` code). – user Apr 01 '17 at 21:05
  • (and then there are [channels](https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html) and [scoped_tls](https://github.com/alexcrichton/scoped-tls/blob/master/src/lib.rs) and stuff, but in the end you still need to wrap up the object in a `Send`-ly way) – user Apr 01 '17 at 21:19
7

It's easy enough to write a similar version yourself, using only tools from the standard library:

use std::thread;
use std::time::Duration;

struct Timer<F> {
    delay: Duration,
    action: F,
}

impl<F> Timer<F>
where
    F: FnOnce() + Send + Sync + 'static,
{
    fn new(delay: Duration, action: F) -> Self {
        Timer { delay, action }
    }

    fn start(self) {
        thread::spawn(move || {
            thread::sleep(self.delay);
            (self.action)();
        });
    }
}

fn main() {
    fn x() {
        println!("hello");
        let t = Timer::new(Duration::from_secs(2), x);
        t.start();
    }

    let t = Timer::new(Duration::from_secs(2), x);
    t.start();

    // Wait for output
    thread::sleep(Duration::from_secs(10));
}

As pointed out by malbarbo, this does create a new thread for each timer. This can be more expensive than a solution which reuses threads but it's a very simple example.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Note that it can be inefficient to allocate a new thread in each `start` invocation. – malbarbo May 03 '16 at 19:50
  • @malbarbo great point! If you want a recurring task, it's **much** better to use the same thread over and over again, which I assume the timer crate from your answer does. I was just trying to match the Python semantics. – Shepmaster May 03 '16 at 20:02