How to Sleep a Thread in Python
You can sleep a thread by calling the time.sleep() function.
In this tutorial you will discover how to sleep a thread in Python.
Let's get started.
Need to Sleep in a Thread
A thread is a thread of execution in a computer program.
Every Python program has at least one thread of execution called the main thread. Both processes and threads are created and managed by the underlying operating system.
Sometimes we may need to create additional threads in our program in order to execute code concurrently.
Python provides the ability to create and manage new threads via the threading module and the threading.Thread class.
You can learn more about Python threads in the guide:
In concurrent programming we may want the current thread to block or sleep for a specified time.
How can we make threads sleep in Python?
What is Sleep
The time.sleep() function will cause the calling thread to block for the specified number of seconds.
Suspend execution of the calling thread for the given number of seconds.
-- time — Time access and conversions
For example:
...
# sleep for 5 seconds
time.sleep(5)
The time.sleep() function takes a floating point number of seconds to sleep.
If a fraction less than zero is provided, then this indicates the number of milliseconds for the calling thread to block.
The argument may be a floating point number to indicate a more precise sleep time.
-- time — Time access and conversions
Recall that there are 1,000 milliseconds in one second. Therefore a sleep value of 0.1 will allow the calling thread to sleep for 1/10th of a second or 100 milliseconds.
...
# sleep for 100 milliseconds
time.sleep(0.1)
We may call the time.sleep() function from the main thread, which is the default thread in your progress. We may also call the time.sleep() function from a new thread used to execute a custom function.
The calling thread may block for less than the time specified, if the process is interrupted.
The actual suspension time may be less than that requested because any caught signal will terminate the sleep() following execution of that signal's catching routine.
-- time — Time access and conversions
This may happen if the user presses Ctrl-C to send a SIGINT (signal interrupt) to the process that owns the thread, which by default will terminate the process.
The calling thread will block for at least the specified number of seconds, but may block for longer.
... the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system.
-- time — Time access and conversions
This is because the underlying operating system decides what threads should run and when to run them. It may decide to let the thread block for a little longer before waking it up and allowing it to resume execution (e.g. a fraction of a second longer).
The time.sleep() function calls a system function to block the calling thread. e.g. the sleep system call. This function call itself may have more or less precision than you require. For example, although you specify a sleep of one or a few milliseconds, the underlying system call may have a precision limited at tens of milliseconds.
This is a capability provided by the underlying operating system and may be implemented differently on different operating systems, such as Windows, Linux and MacOS.
Next, let's consider why we might sleep a thread.
Why Use Sleep
There are many reasons why we may want a thread to sleep.
For example:
- Add a delay.
- Slow down execution of a loop.
- Allow other threads to run.
- Force race conditions during debugging.
Let's take a closer look at each in turn.
Sleep to Introduce a Delay
We may want to introduce a delay into our program.
This may be to avoid overwhelming an external resource, such as:
- Making too many connections to a web server
- creating too many files too quickly.
- Sending many emails too fast.
For example, too many rapid sequential operations on an external resource may trigger computer security measures to protect the resource from a denial of service attack.
It may be to simply slow down a process to permit the user or another thread to take notice and respond to changes within the application.
Sleep to Slow-Down a Loop
We may introduce a sleep to slow down the execution of a loop.
This may be to make the program more usable, such as to allow the user to see and read messages printed each iteration.
It may also be to avoid overwhelming an external resource that is used each iteration of the loop.
Sleep to Let Other Threads Run
Calling sleep will block the current thread.
This will signal to the underlying operating system that the thread is a candidate for a context switch.
You may recall that modern operating systems like Windows, MacOS and Linux achieve multitasking via context switching threads. This is where running threads are suspended and suspended threads are resumed. This process of context switching between threads is occurring continuously by the operating system to simulate the running of multiple programs and program threads concurrently.
Calling sleep is one way to allow other threads in the Python application or other threads on the system to run.
This may be achieved with a call to time.sleep() with zero seconds, for example:
...
# allow other threads to run
time.sleep(0)
Sleep for Debugging
Adding sleeps to a program can help expose bugs.
Race conditions can occur if two threads attempt to read or modify unprotected variables concurrently or to coordinate with wait/notify.
Often, race conditions are not exposed until some unexpected delay is introduced into a program, such as a resource taking an unusually long time.
We can stress test our concurrent applications and help expose race conditions by adding sleeps to the code while testing.
For example, if one thread is waiting to be notified on a threading.Condition by another thread, and the oher thread calls notify() before the waiting thread calls wait() then this indicates a race condition and the waiting thread will never be notified.
We may be able to force this race condition by adding a call to time.sleep() prior to the waiting thread calling wait() to simulate a natural context-switching delay and see if the wait-notify mechanism used in the program still operates correctly.
You can learn more about this type of race condition in this tutorial:
Next, let's take a closer look at how to sleep.
How to Sleep in a Thread
We can sleep a thread by calling the time.sleep() function from the thread.
For example:
...
# sleep the current thread for 10 seconds
time.sleep(10)
The sleep function takes a floating point value for the number of seconds to sleep, where fractions of a second are milliseconds.
For example:
...
# sleep the current thread for 10 milliseconds
time.sleep(0.010)
We can sleep for longer intervals such as minutes and days by specifying these intervals in terms of the number of seconds.
Let's make this clear with a few examples.
Note: You may recall that although we specify a number of seconds to sleep, that the program may sleep for longer as resuming the thread is at the whim and capabilities of the underlying operating system.
How to Sleep for Milliseconds
There are 1,000 milliseconds in one second.
We can specify a time interval as a floating point fraction of a second in order to sleep for milliseconds.
For example:
- 1,000 milliseconds is 1 second.
- 100 milliseconds is 0.1 seconds.
- 10 milliseconds is 0.01 seconds.
- 1 millisecond is 0.001 seconds.
The example below demonstrates how to sleep for half a second or 500 milliseconds.
# SuperFastPython.com
# example of sleeping for milliseconds
from time import sleep
# report a message
print('Sleeping for half a second')
# block
sleep(0.5)
We can update the program to run once every 500 milliseconds or twice per second by calling sleep in a loop.
For example:
# SuperFastPython.com
# example of running twice per minute
from time import sleep
# run forever
while True:
# do a task
# ...
# report a message
print('Sleeping for half a second')
# block
sleep(0.5)
How to Sleep for Seconds
The time.sleep() function takes a number of seconds to sleep as an argument directly.
For example, we may sleep for 30 seconds by providing the value of 30 to the function.
# SuperFastPython.com
# example of sleeping for seconds
from time import sleep
# report a message
print('Sleeping for 30 seconds')
# block
sleep(30)
We can update the program to run once every 30 seconds or twice per minute by calling sleep in a loop.
For example:
# SuperFastPython.com
# example of running twice per minute
from time import sleep
# run forever
while True:
# do a task
# ...
# report a message
print('Sleeping for 30 seconds')
# block
sleep(30)
How to Sleep for Minutes
We can sleep for a given number of minutes by specifying the interval in terms of seconds.
For example:
- 1 minute is 60 seconds.
- 10 minutes is 600 seconds.
- 100 minutes is 6,000 seconds.
It may be easier to specify the interval as a multiplication, e.g. 2 minutes and 30 seconds could be specified as 2.5 * (60), which is 150 seconds.
The example below demonstrates how to sleep for 5 minutes specified as a multiplication.
# SuperFastPython.com
# example of sleeping for minutes
from time import sleep
# report a message
print('Sleeping for 5 minutes')
# block
sleep(5 * 60)
We can update the program to run once every 5 minutes or 12 times per hour by calling sleep in a loop.
For example:
# SuperFastPython.com
# example of running twelve times per hour
from time import sleep
# run forever
while True:
# do a task
# ...
# report a message
print('Sleeping for 5 minutes')
# block
sleep(5 * 60)
How to Sleep for Hours
We may want to sleep for hours.
This may be helpful to perform an action in the application every hour or twice per day.
This can be achieved by specifying the number of hours to sleep in terms of seconds.
For example:
- 1 hour is 3,600 seconds
- 10 hours is 36,000 seconds
- 100 hours is 360,000 seconds
The sleep interval may be easier to specify in your program as a multiplication. For example, 2 hours and 30 minutes can be specified as 2.5 * (60 * 60), brackets added for clarity.
For example:
- 1 hour: 1 * (60*60)
- 10 hours: 10 * (60*60)
- 100 hours: 100 * (60*60)
The example below demonstrates how to sleep for 12 hours.
# SuperFastPython.com
# example of sleeping for hours
from time import sleep
# report a message
print('Sleeping for 12 hours')
# block
sleep(12 * (60*60))
We can update the program to run once every 12 hours or twice per day by calling sleep in a loop.
For example:
# SuperFastPython.com
# example of running twice per day
from time import sleep
# run forever
while True:
# do a task
# ...
# report a message
print('Sleeping for 12 hours')
# block
sleep(12 * (60*60))
How to Sleep for Days
We may need our thread to sleep for days.
This may be helpful to perform an action once per day or once per week.
This can be achieved by specifying the number of days to sleep in terms of seconds.
For example:
- 1 day is 86,400 seconds
- 10 days is 864,000 seconds
- 100 days is 8,640,000 seconds
The interval may be easier to specify in your program as a multiplication. For example, 2 days and 12 hours can be specified as 2.5 * (60 * 60 * 24), brackets added for clarity.
- 1 hour: 1 * (60*60*24)
- 10 hours: 10 * (60*60*24)
- 100 hours: 100 * (60*60*24)
The example below demonstrates how to sleep for 7 days.
# SuperFastPython.com
# example of sleeping for days
from time import sleep
# report a message
print('Sleeping for 7 days')
# block
sleep(7 * (60*60*24))
We can update the program to run once a week by calling sleep in a loop.
For example:
# SuperFastPython.com
# example of running once per week
from time import sleep
# run forever
while True:
# do a task
# ...
# report a message
print('Sleeping for 7 days')
# block
sleep(7 * (60*60*24))
Next, let's consider some tips when using sleep in your program.
Tips When Using Sleep
This section provides some times when using the time.sleep() function in your program.
They are:
- Sleep will not release acquired locks.
- Use sleep in a busy-wait loop.
- You can sleep for zero seconds.
- Consider waiting instead of sleeping.
- Protect against SIGINT.
Let's take a closer look at each in turn.
Sleep Will Not Release Locks
In concurrent programming we can access critical sections of code by first acquiring a lock, such as a mutual exclusion (mutex) lock.
We may also acquire a condition which will acquire its internal lock, or acquire a semaphore which will modify an internal counter protected by a lock.
If we call time.sleep() while holding a lock, it will not release the resource.
For example:
...
# acquire a mutex
with lock:
# do something
...
# sleep for a while
sleep(60)
Sleeping while holding the lock will prevent any other thread from acquiring the lock. The problem with this is that it may result in extended delays within your program.
You should avoid this, unless it is a specific requirement of your program.
Ideally, you would release any locks before putting a thread to sleep.
For example:
...
# acquire a mutex
with lock:
# do something
...
# sleep for a while
sleep(60)
Use a Sleep When Busy-Waiting
Busy waiting, also called spinning, refers to a thread that repeatedly checks a condition in a loop.
It is a form of waiting that invokes continual checking of the desired state, rather than blocking and allowing other threads to run.
We may use busy waiting when trying to coordinate two threads based on timing, to avoid missing an event or change in state.
You can learn more about busy waits in this tutorial:
A spinlock is a specific type of busy waiting used to wait for a mutual exclusion lock.
You can learn more about Spinlocks in this tutorial:
For example, the following loop demonstrates a spinlock where the busy wait loop tries to acquire a mutex lock each iteration.
...
# example of a busy wait loop for a spinlock
while True:
# try and get the lock
acquired = lock.acquire(blocking=False)
# check if we got the lock
if acquired:
# stop waiting
break
The computational burden of a busy wait loop can be lessened by adding sleep.
This will delay the checking of the desired state each iteration of the loop, but will force the thread to block and allow other threads to run, perhaps improving overall performance.
The example below demonstrates adding a delay with a call to time.sleep() to a spinlock.
For example:
...
# example of a busy wait loop for a spinlock with a sleep
while True:
# try and get the lock
acquired = lock.acquire(blocking=False)
# check if we got the lock
if acquired:
# stop waiting
break
else:
# sleep for a moment
sleep(0.1)
Sleep for Zero Seconds
You can add a call to time.sleep() for zero seconds.
For example:
...
# sleep for zero seconds
time.sleep(0)
At first glance, this may look like a “no operation” (noop), e.g. useless and that the compiler may optimize the call out of existence.
Instead, this is a useful trick in concurrent programming.
Specifically, it can be used in computationally intensive threads or threads that are performing CPU intensive work in a loop.
Adding a sleep for zero seconds will signal to the operating system that the thread may be context switched to another thread. The operating system may choose whether or not to honor this switch.
Nevertheless, it may allow a computationally intensive thread a momentary break and allow other threads in the application to progress, perhaps improving overall performance.
Consider Wait Instead of Sleep
We often add a call to time.sleep() to add a delay to the application.
Instead, we may want to block by waiting instead.
This can be achieved by using a concurrency primitive such as a threading.Condition and calling the wait() function with or without a timeout in seconds. The calling thread will block, just like calling a sleep(), but the difference is another thread may call the notify() function on the condition in order to wake up the blocking thread.
For example, we can wait on a condition with a timeout:
...
# acquire the condition
with condition:
# block for 10 seconds or until notified
condition.wait(timeout=10)
We may wait on many concurrency primitives, such as:
- Mutual exclusion locks and reentrant locks via the acquire() function.
- Events via wait().
- Conditions via wait().
- Barriers via wait().
- Semaphores via acquire().
You can learn more about blocking calls and waiting in this tutorial:
Importantly, when calling the wait() function such as a condition, the lock on the primitive is released until the thread is resumed. This is helpful if other threads must acquire the lock in order for the program to progress.
Protect Against Terminal Signals
A call to time.sleep() may be interrupted by a signal.
For example, the user may press Control-C in order to send the SIGINT interrupt signal to the application. This may cause the sleeping thread to raise an exception and in turn terminate the process.
You may want to protect your call to sleep from being interrupted.
This can be achieved by registering a signal handler and taking appropriate action, such as closing the application cleanly once the sleep operation has completed.
This involves first defining a function to be called when the signal is received. The function takes the signal number and the current stack frame.
# handle sigint
def custom_handler(signum, frame):
print('Got a SIGINT, ignoring for now...')
The handler can then be registered via the signal.signal() function that first specifies the signal to respond to, such as signal.SIGINT (interrupted with Control-C) and the function to call.
For example:
...
# register the signal handler for control-c
signal(SIGINT, custom_handler)
The example below demonstrates this, handling Control-C signals if they occur while sleeping and preventing the program from terminating.
# SuperFastPython.com
# example of protecting a sleep from signal interruption
from time import sleep
from signal import signal
from signal import SIGINT
# handle sigint
def custom_handler(signum, frame):
print('Got a SIGINT, ignoring for now...')
# register the signal handler for control-c
signal(SIGINT, custom_handler)
# report a message
print('Sleeping for a moment')
# block
sleep(5)
print('All done')
Running the program will sleep for five seconds and report a message.
For example:
Sleeping for a moment
All done
If you press Control-C while the program is running, e.g. while sleeping, then the program will handle the SIGINT signal, report a message, and will continue waiting until the sleep is complete.
For example:
Sleeping for a moment
^C
Got a SIGINT, ignoring for now...
All done
Takeaways
You now know how to sleep a thread.
If you enjoyed this tutorial, you will love my book: Python Threading Jump-Start. It covers everything you need to master the topic with hands-on examples and clear explanations.