Simple thread handling in Python
Sometimes you find yourself in need of two or more application loops. This is why we have threads. Here is a simple way to implement them.
An application loop, in case you didn’t grow up with the same programming manuals as I did, is a usually infinite loop that keeps the program repeating until it’s exited. In Python, it usually goes like:
while True:
print ('Main thread');
This will flood your screen with this silly little message until you hit Ctrl-C. Let’s make it a little more programlike:
from time import sleep
x = 0
while True:
x += 1
print("Main thread " + str(x))
sleep(1)
Now we’ll see a single line every second, and an incremental number value. Wow, very advanced!
Let’s launch a second thread now. This one will do the exact same thing, but introduce itself as “Secondary thread”. Let’s put it into a def
for the time being:
def start_secondary():
y = 0
while True:
y += 1
print("Secondary thread " + str(y))
sleep(0.5)
Why is y
defined inside the def
, and not as a global variable, like x
? It is because this variable will belong to the secondary thread, and not the main thread. Threads can’t use each others’ variables, at least not in such a straightforward way.
Now we need to run this def
, and its infinite loop, on a separate thread. We will need the threading
library for this.
import threading
secondary_thread = threading.Thread(target = start_secondary)
secondary_thread.start()
It really is this simple. Now Python will launch a second thread, and we’ll see Secondary thread n
appearing every 0.5 seconds. Here is the complete source:
import threading
from time import sleep
x = 0
def start_secondary():
y = 0
while True:
y += 1
print("Secondary thread " + str(y))
sleep(0.5)
secondary_thread = threading.Thread(target = start_secondary)
secondary_thread.start()
while True:
x += 1
print("Main thread " + str(x))
sleep(1)
The output will be something like this:
Secondary thread 1
Main thread 1
Secondary thread 2
Main thread 2
Secondary thread 3
Secondary thread 4
Main thread 3
Secondary thread 5
Secondary thread 6
Main thread 4
But wait! Something is wrong. When you stop your program with Ctrl-C
, the main thread indeed exits, but the secondary thread will not stop! It keeps flooding the screen and ignore Ctrl-C. What now?
Your instincts may tell you to throw the computer out of the window, and call it a day. That’s a very wise decision, and it would certainly improve your mental well-being on the long run. Just be sure to hit someone from HR, having a smoke down there.
Other than that, you can shut down the task, and try again. This time, add this little line before starting the thread:
secondary_thread.daemon = True
This turns the thread into a daemon, which automatically shuts down the secondary thread when the main thread exits.
This solution comes handy when you need multiple listeners in your program. For example, you want the main thread to do its main thread things, while have a watcher on your secondary thread, waiting for updates from a cloud database.
How to stop a thread? It’s not as straightforward as starting one, as there is no mechanism in Python for that. However, you can use a thread event as a flag to tell secondary_thread
to kill itself. The flag has to be passed as an argument.
def start_secondary(stop_event):
y = 0
while not stop_event.is_set():
y += 1
print("Secondary thread " + str(y))
time.sleep(0.5)
stop_event = threading.Event()
secondary_thread = threading.Thread(target=start_secondary, args=(stop_event))
secondary_thread.daemon = True
secondary_thread.start()
Now you can stop the secondary thread from the main thread easily:
stop_event.set()
Killing it from inside the thread is even easier, just exit the infinite loop.
By the way, this is also how you can pass methods from the main thread to the secondary thread: by adding them as args
to your thread launcher. But I guess a smart kitten like you already figured that out.
There is a lot more to threads! For example, threads may be competing for resources, which can cause trouble. To avoid race conditions, you can use thread locking. But this is beyond the scope of this article.
A thread’s execution may also be stopped to wait until another one finishes running with the .join()
method. In the current example it wouldn’t be a good idea because both threads are running in an infinite loop.
Some programmers may find the above example somewhat outdated. Indeed, there is ThreadPoolExecutor
now, which is a whole different animal. The intent with this article was to show a basic example of multithreading, not to teach the entire course. For that matter, threads are such a complex topic, you can take entire courses on them, and write a PhD. Again, this is something you shouldn’t expect from a meager tech blog.