Threading is a way to run multiple threads concurrently within a single process. This can be useful for executing tasks in parallel and improving the performance of your Python program.
In this article, we will cover the basics of threading in Python and show you how to create and start threads. We will also discuss some common pitfalls to avoid when working with threads.
Creating a Thread
To create a thread in Python, we first need to import the threading
module. Then, we can define a new class that inherits from the Thread
class and overrides the run
method. The run
method is the entry point for the thread and will contain the code that the thread will execute.
Here’s an example of a simple thread that prints a message:
import threading
class PrintThread(threading.Thread):
def __init__(self, message):
super().__init__()
self.message = message
def run(self):
print(self.message)
Starting a Thread
To start a thread, we first need to create an instance of our PrintThread
class and then call the start
method. The start
method will create a new native thread and run the run
method in the new thread.
Here’s an example of how to start a PrintThread
:
thread = PrintThread("Hello from the thread!")
thread.start()
It’s important to note that the start
method only initiates the execution of the thread. It does not wait for the thread to finish before moving on to the next line of code.
Joining a Thread
If you want your main thread to wait for a worker thread to finish before moving on, you can use the join
method. The join
method blocks the calling thread until the thread it is called on finishes execution.
Here’s an example of how to use the join
method:
thread = PrintThread("Hello from the thread!")
thread.start()
thread.join()
print("Thread finished!")
In this example, the main thread will wait for the PrintThread
to finish before printing the message “Thread finished!”.
Common Pitfalls
There are a few things to be aware of when working with threads in Python:
- Python’s
global interpreter lock
(GIL) prevents multiple native threads from executing Python bytecodes at once. This means that only one thread can execute Python code at a time, even if you have multiple CPU cores. - The
join
method only works for threads that are started using thestart
method. If you create a thread using therun
method instead of thestart
method, thejoin
method will have no effect. - It’s generally a good idea to avoid shared state between threads. This can lead to race conditions and other synchronization issues. If you need to share data between threads, consider using a
Queue
or other thread-safe data structure.
Conclusion
In this article, we covered the basics of threading in Python. We learned how to create and start threads, and how to use the join
method to wait for a thread to finish. We also discussed some common pitfalls to avoid when working with threads.
With this knowledge, you should be able to start using threads in your Python programs to improve their performance and execute tasks in parallel.
Exercises
To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.
Write a program that creates a thread that prints the numbers from 1 to 10.
import threading
class PrintThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
for i in range(1, 11):
print(i)
thread = PrintThread()
thread.start()
thread.join()
Write a program that creates a thread that calculates the sum of the numbers from 1 to 100. Print the result after the thread finishes.
import threading
class SumThread(threading.Thread):
def __init__(self):
super().__init__()
self.sum = 0
def run(self):
for i in range(1, 101):
self.sum += i
thread = SumThread()
thread.start()
thread.join()
print(thread.sum) # Output: 5050
Write a program that creates a thread that calculates the factorial of a given number. The number should be passed as a command line argument. Print the result after the thread finishes.
import threading
import sys
class FactorialThread(threading.Thread):
def __init__(self, number):
super().__init__()
self.number = number
self.result = 1
def run(self):
for i in range(2, self.number + 1):
self.result *= i
number = int(sys.argv[1])
thread = FactorialThread(number)
thread.start()
thread.join()
print(thread.result)
Write a program that creates a thread that counts down from 10 to 1. Print each number on a separate line. Use the sleep
method from the time
module to pause the thread for 1 second between each number.
import threading
import time
class CountdownThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
for i in range(10, 0, -1):
print(i)
time.sleep(1)
thread = CountdownThread()
thread.start()
thread.join()
Write a program that creates two threads: one that prints the numbers from 1 to 10, and another that prints the letters from ‘a’ to ‘j’. Use the join
method to make sure that both threads finish before the program exits.
import threading
class PrintNumbersThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
for i in range(1, 11):
print(i)
class PrintLettersThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
for letter in 'abcdefghij':
print(letter)
numbers_thread = PrintNumbersThread()
letters_thread = PrintLettersThread()
numbers_thread.start()
letters_thread.start()
numbers_thread.join()
letters_thread.join()
This program will create two threads, one that prints the numbers from 1 to 10 and another that prints the letters from ‘a’ to ‘j’. The join
method is called on both threads to make sure that they both finish before the program exits. The output of this program will be the numbers from 1 to 10 and the letters from ‘a’ to ‘j’ printed in an interleaved fashion.