Python Threads
Python includes several methods for creating threads. The simplest and most flexible is to create a new Thread
object using the threading library. When that object is created, we can give it a function to use as a starting point for the thread.
Here’s a quick example of threads in Python:
import threading
import time
import sys
class MyThread:
def __init__(self, name):
"""Constructor.
Args:
name: the name of the thread
"""
self.__name = name
def run(self):
"""Thread method."""
for i in range(0, 3):
print("{} : iteration {}".format(self.__name, i))
# tell the OS to wake this thread up after at least 1 second
time.sleep(1)
@staticmethod
def main(args):
# create threads
t1_object = MyThread("Thread 1")
thread1 = threading.Thread(target=t1_object.run)
t2_object = MyThread("Thread 2")
thread2 = threading.Thread(target=t2_object.run)
t3_object = MyThread("Thread 3")
thread3 = threading.Thread(target=t3_object.run)
# start threads
print("main: starting threads")
thread1.start()
thread2.start()
thread3.start()
# wait until all threads have terminated
print("main: joining threads")
thread1.join()
thread2.join()
thread3.join()
print("main: all threads terminated")
# main guard
if __name__ == "__main__":
MyThread.main(sys.argv)
Let’s look at this code piece by piece so we fully understand how it works.
Imports
import threading
import time
import sys
We import both the threading
library, which allows us to create threads, as well as the time
library to put threads to sleep. We’ll also need the sys
library to access command-line arguments, if any are used.
Class Declaration
class MyThread:
def __init__(self, name):
self.__name = name
The class is very simple. Inside of the constructor, we are simply setting a name
attribute so we can tell our threads apart.
Run Method
def run(self):
for i in range(0, 3):
print("{} : iteration {}".format(self.__name, i))
# tell the OS to wake this thread up after at least 1 second
time.sleep(1)
The run()
method is the method we’ll use to start our threads. This method is pretty short - it simply iterates 3 times and prints the value of the iteration along with the thread’s name, and then it uses the time.sleep(1)
method call. This tells the operating system to put this thread into a waiting state, and to not wake it up until at least 1 second has elapsed. Of course, we can’t guarantee that the operating system won’t make this thread wait even longer than that, but typically it will happen so fast that we won’t be able to tell the difference.
Main Method
@staticmethod
def main(args):
# create threads
t1_object = MyThread("Thread 1")
thread1 = threading.Thread(target=t1_object.run)
t2_object = MyThread("Thread 2")
thread2 = threading.Thread(target=t2_object.run)
t3_object = MyThread("Thread 3")
thread3 = threading.Thread(target=t3_object.run)
# start threads
print("main: starting threads")
thread1.start()
thread2.start()
thread3.start()
# wait until all threads have terminated
print("main: joining threads")
thread1.join()
thread2.join()
thread3.join()
print("main: all threads terminated")
Finally, the main()
method will create three instances of the threading.Thread
class, and provide an instance of our MyThread
class as the target
argument to the constructor. In effect, we are wrapping our runnable class in a thread.
Then, we call the start()
method on the thread, which will actually create the thread through the operating system and start it running. Notice that we do not call the run()
method directly - that is called for us once the thread is created in the start()
method.
Finally, we call the join()
method on each thread. The join()
method will block this thread until the thread we called it on has terminated. So, by calling the join()
method on each of the three threads, we are making sure that they have all finished their work before the main thread continues.
That’s all there is to this example!
Execution
When we execute this example, we can see many different outputs, depending on how the threads are scheduled with the operating system. Below are a few that were observed when this program was executed during testing.
If you look closely at these four lists, no two of them are exactly the same. This is because of how the operating system schedules threads - we cannot predict how it will work, and because of this a multithreaded program could run differently each time it is executed!