Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resource usage of "time.sleep" in loop vs. "threading.Timer"

First method:

import threading
import time

def keepalive():
    while True:
        print 'Alive.'
        time.sleep(200)
threading.Thread(target=keepalive).start()

Second method:

import threading

def keepalive():
    print 'Alive.'
    threading.Timer(200, keepalive).start()

threading.Timer(200, keepalive).start()

Which method takes up more RAM? And in the second method, does the thread end after being activated? or does it remain in the memory and start a new thread? (multiple threads)

like image 211
Taha Avatar asked Nov 07 '12 00:11

Taha


2 Answers

Timer creates a new thread object for each started timer, so it certainly needs more resources when creating and garbage collecting these objects.

As each thread exits immediately after it spawned another active_count stays constant, but there are constantly new Threads created and destroyed, which causes overhead. I'd say the first method is definitely better.

Altough you won't realy see much difference, only if the interval is very small.

like image 171
mata Avatar answered Oct 12 '22 22:10

mata


Here's an example of how to test this yourself:

And in the second method, does the thread end after being activated? or does it remain in the memory and start a new thread? (multiple threads)

import threading

def keepalive():
    print 'Alive.'
    threading.Timer(200, keepalive).start()
    print threading.active_count()

threading.Timer(200, keepalive).start()

I also changed the 200 to .2 so it wouldn't take as long.

The thread count was 3 forever.

Then I did this:

top -pid 24767

The #TH column never changed.

So, there's your answer: We don't have enough info to know whether Python maintains a single timer thread for all of the timers, or ends and cleans up the thread as soon as the timer runs, but we can be sure the threads doesn't stick around and pile up. (If you do want to know which of the former is happening, you can, e.g., print the thread ids.)

An alternative way to find out is to look at the source. As the documentation says, "Timer is a subclass of Thread and as such also functions as an example of creating custom threads". The fact that it's a subclass of Thread already tells you that each Timer is a Thread. And the fact that it "functions as an example" implies that it ought to be easy to read. If you click the link form the documentation to the source, you can see how trivial it is. Most of the work is done by Event, but that's in the same source file, and it's almost as simple. Effectively, it just creates a condition variable, waits on it (so it blocks until it times out, or you notify the condition by calling cancel), then quits.

The reason I'm answering one sub-question and explaining how I did it, rather than answering each sub-question, is because I think it would be more useful for you to walk through the same steps.

On further reflection, this probably isn't a question to be decided by optimization in the first place:

If you have a simple, synchronous program that needs to do nothing for 200 seconds, make a blocking call to sleep. Or, even simpler, just do the job and quit, and pick an external tool to schedule your script to run every 200s.

On the other hand, if your program is inherently asynchronous—especially if you've already got thread, signal handlers, and/or an event loop—there's just no way you're going to get sleep to work. If Timer is too inefficient, go to PyPI or ActiveState and find a better timer that lets you schedule repeatable timers (or even multiple timers) with a single instance and thread. (Or, if you're using signals, use signal.alarm or setitimer, and if you're using an event loop, build the timer into your main loop.)

I can't think of any use case where sleep and Timer would both be serious contenders.

like image 24
abarnert Avatar answered Oct 13 '22 00:10

abarnert