7

I'm performing realtime data processing + display, and I hit our database every 60 seconds. I'd like to not use time.sleep() for waiting every 60 seconds, as it removes control from me (namely REPL access to variables, which isn't necessary but nice) and freezes matplotlib charts.

Is there an alternative? Ideally, something that would initially give control to the user, and after 60 seconds, take control away, run some code, and update a plot, then give control back to the user. (When I say control, I mean REPL control).

Any ideas?

5

2 Answers 2

13

If you don't need to take away user control, there's a very easy way to do this: Create a threading.Timer.

What you want to do is take the "continuation" of the function—that is, everything that would come after the time.sleep—and move it into a separate function my_function, then schedule it like this:

threading.Timer(60, my_function).start()

And at the end of my_function, it schedules a new Timer with the exact same line of code.

Timer is a pretty clunky interface and implementation, but it's built into the stdlib. You can find recipes on ActiveState and modules on PyPI that provide better classes that, e.g., run multiple timers on one thread instead of a thread per timer, let you schedule recurring calls so you don't have to keep rescheduling yourself, etc. But for something that just runs every 60 seconds, I think you may be OK with Timer.

One thing to keep in mind: If the background job needs to deal with any of the same data the user is dealing with in the REPL, there is a chance of a race condition. Often in an interactive environment (especially in Python, thanks to the GIL), you can just lay the onus on the user to not cause any races. If not, you'll need some kind of synchronization.

Another thing to keep in mind: If you're trying to do GUI work, depending on the GUI you're using (I believe matplotlib is configurable but defaults to tkinter?), you may not be able to update the GUI from a background thread.

But there's actually a better solution in that case anyway. GUI programs have an event loop that runs in some thread or other, and almost every event loop ever design has a way to schedule a timer in that thread. For tkinter, if you have a handle to the root object, just call root.after(60000, my_function) instead of threading.Timer(60, my_function).start(), and it will run on the same thread as the GUI, and without wasting any unnecessary resources.

3
  • 1
    This is what I needed. I feel elated about this new tool. Thanks! Commented Mar 1, 2013 at 21:46
  • One thing: Is there anyway to make the printed statements persist on screen? As it is, the printed statements appear, but hitting enter or typing something in the REPL will remove them. Commented Mar 1, 2013 at 22:19
  • I don't know which printed statements you're talking about, but I'm guessing either (a) you're printing stuff without a newline, so the prompt is overwriting it, or (b) your REPL is something graphical?
    – abarnert
    Commented Mar 1, 2013 at 22:40
1

if you would like program to exit before child finish processing you must set:

curr_thread.daemon = True

Daemon threads in Python operate in the background, running concurrently with the main program. Their distinctive feature lies in their non-blocking nature, allowing the main program to proceed without waiting for their completion.

It means that main thread will not wait for daemon thread.

In this case main thread will sacrifice its child.

New contributor
Miłosz Kaszyński is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.