63

I am writing an queue processing application which uses threads for waiting on and responding to queue messages to be delivered to the app. For the main part of the application, it just needs to stay active. For a code example like:

while True:
  pass

or

while True:
  time.sleep(1)

Which one will have the least impact on a system? What is the preferred way to do nothing, but keep a python app running?

Gavin M. Roy
  • 4,551
  • 4
  • 33
  • 29
  • 9
    The correct answer is not to do polling I/O at all. See for instance the select() call, where the OS sleeps until there's something ready to read. – Charles Duffy Feb 09 '09 at 18:03
  • Well in the case of the threads, they're using blocking TCP connections waiting on messages. Its just the main thread that I am concerned about, and it doesn't do anything but handle the command line options, read config and kick of threads. – Gavin M. Roy Feb 09 '09 at 20:14

8 Answers8

92

I would imagine time.sleep() will have less overhead on the system. Using pass will cause the loop to immediately re-evaluate and peg the CPU, whereas using time.sleep will allow the execution to be temporarily suspended.

EDIT: just to prove the point, if you launch the python interpreter and run this:

>>> while True:
...     pass
... 

You can watch Python start eating up 90-100% CPU instantly, versus:

>>> import time 
>>> while True:
...     time.sleep(1)
... 

Which barely even registers on the Activity Monitor (using OS X here but it should be the same for every platform).

Jay
  • 41,768
  • 14
  • 66
  • 83
34

Why sleep? You don't want to sleep, you want to wait for the threads to finish.

So

# store the threads you start in a your_threads list, then
for a_thread in your_threads:
    a_thread.join()

See: thread.join

tzot
  • 92,761
  • 29
  • 141
  • 204
  • 1
    I am not expecting threads to finish, though. This is a non-ending process. Does this logic still apply? – Gavin M. Roy Feb 09 '09 at 20:13
  • Of course the logic still applies. You would avoid wasting even the few cycles for a "looped" sleep. – tzot Feb 09 '09 at 20:48
  • This doesn't seem to work, since the threads should not terminate the process is terminated, the timeout could conceptually always come first, or am I missing something? – Gavin M. Roy Feb 09 '09 at 22:02
  • 1
    @Crad -- what timeout? The main thread is waiting forever for children. This is what main threads do. – S.Lott Feb 10 '09 at 02:45
  • 1
    @Crad: in my sample code I did not specify any timeout, so the join will wait forever. Do you have a reason to specify one yourself? – tzot Feb 10 '09 at 15:11
32

If you are looking for a short, zero-cpu way to loop forever until a KeyboardInterrupt, you can use:

from threading import Event

Event().wait()

Note: Due to a bug, this only works on Python 3.2+. In addition, it appears to not work on Windows. For this reason, while True: sleep(1) might be the better option.

For some background, Event objects are normally used for waiting for long running background tasks to complete:

def do_task():
    sleep(10)
    print('Task complete.')
    event.set()

event = Event()
Thread(do_task).start()
event.wait()

print('Continuing...')

Which prints:

Task complete.
Continuing...
Matthew D. Scholefield
  • 2,977
  • 3
  • 31
  • 42
  • 4
    Note that this works on Linux and Mac, but on Windows the the KeyboardInterrupt will *not* interrupt the event.wait(). If a wait timeout is specified, the exception will raise at the end of the timeout, but if none is given then event.wait() will block forever. On Windows, a SIGINT *will* interrupt a sleep, so that's the better implementation, there. I haven't seen this documented anywhere, but it's been my experience in testing (Python 3.7). I found a related thread here: https://stackoverflow.com/questions/39545612/cannot-interrupt-lock-acquire-whereas-i-can-interrupt-time-sleep – joshstaiger Jul 26 '18 at 22:07
  • @joshstaiger Interesting. I've updated the post. I did find that due to a bug in Python 2, it didn't work, but I would have never guessed it wouldn't work with 3.2+ in Windows. – Matthew D. Scholefield Jul 27 '18 at 06:25
  • @joshstaiger very good catch. found py-issue: https://bugs.python.org/issue35935. ran into this when looking at slack-bolt library. its [start()](https://slack.dev/bolt-python/api-docs/slack_bolt/adapter/socket_mode/base_handler.html#slack_bolt.adapter.socket_mode.base_handler.BaseSocketModeHandler.start) func has a special case for windows; uses `signal.signal(signal.SIGINT, signal.SIG_DFL)`. wouldn't recommend this though, because the default action for SIGINT is to close the whole python program. doesn't just raise `KeyboardInterrupt` as you might think. 'while(1) sleep` approach is better – starwarswii May 31 '22 at 21:14
12

signal.pause() is another solution, see https://docs.python.org/3/library/signal.html#signal.pause

Cause the process to sleep until a signal is received; the appropriate handler will then be called. Returns nothing. Not on Windows. (See the Unix man page signal(2).)

Adam
  • 597
  • 6
  • 11
  • You shouldn't really do this --- it can cause problems when in threads. `threading.Event().wait()` is much better. – DUO Labs May 17 '22 at 14:20
6

I've always seen/heard that using sleep is the better way to do it. Using sleep will keep your Python interpreter's CPU usage from going wild.

hernan43
  • 805
  • 1
  • 8
  • 18
6

You don't give much context to what you are really doing, but maybe Queue could be used instead of an explicit busy-wait loop? If not, I would assume sleep would be preferable, as I believe it will consume less CPU (as others have already noted).

[Edited according to additional information in comment below.]

Maybe this is obvious, but anyway, what you could do in a case where you are reading information from blocking sockets is to have one thread read from the socket and post suitably formatted messages into a Queue, and then have the rest of your "worker" threads reading from that queue; the workers will then block on reading from the queue without the need for neither pass, nor sleep.

Johan
  • 636
  • 5
  • 11
  • Thanks for the tip on Queue looks handy, in this app I am using blocking sockets to listen for messages from an AMQP broker (in separate threads) and acting on them upon receipt. – Gavin M. Roy Feb 09 '09 at 17:31
  • @Crad: Then use select to wait on a socket, similar to the way a queue works. Do not use busy waiting or polling. – S.Lott Feb 10 '09 at 02:41
1

Running a method as a background thread with sleep in Python:

    import threading
    import time


    class ThreadingExample(object):
        """ Threading example class
        The run() method will be started and it will run in the background
        until the application exits.
        """

        def __init__(self, interval=1):
            """ Constructor
            :type interval: int
            :param interval: Check interval, in seconds
            """
            self.interval = interval

            thread = threading.Thread(target=self.run, args=())
            thread.daemon = True                            # Daemonize thread
            thread.start()                                  # Start the execution

        def run(self):
            """ Method that runs forever """
            while True:
                # Do something
                print('Doing something imporant in the background')

                time.sleep(self.interval)

    example = ThreadingExample()
    time.sleep(3)
    print('Checkpoint')
    time.sleep(2)
    print('Bye')
Asclepius
  • 57,944
  • 17
  • 167
  • 143
Ashish Gupta
  • 1,153
  • 12
  • 14
0

Utilizing thread.join() when threads have not been manually stored.

for thread in filter(lambda x: x != threading.current_thread(), threading.enumerate()):
    thread.join()