In this tutorial, we will understand the concept of threading in Python. Let us begin by defining the term thread.
A
threadis a lightweight execution unit that can be managed independently by a scheduler consisting of itsprogram counter, astack, and aregisterset.
register is a temporary storage unit built inside a CPU.program counter is a register that contains the address of the executing instruction.Stack is a reserved region of memory for a thread. When a function executes, it pushes the local variables to the stack, and when the function exits, it pops those variables from the stack.Since each thread has its resource, multiple processes can be executed parallelly by increasing the number of threads in a process, as depicted below.

Threads improve the performance of the processes through parallelism and concurrency.
Threads shares code, data, and other resources like files:
Which allows multiple tasks to be executed parallelly.
For example, in
Antivirus software, there is a thread for each of the following, which runs in parallel.
Creating, managing, and context switches are much faster than performing the same tasks for processes serially.
Threads of a process shares the global variables. So any change in them will be reflected in other threads as well. A thread can also have its own set of local variables.
In case we have limited resources and we want them to be shared, i.e, by synchronization, for better utilization of resources.
A Complex task can be optimized by dividing them into multiple independent subtasks. Each subtask is handled by a thread. These threads will run on separate processors, thus using parallelism to reduce execution time.
CPU Bound Processes: Processes that spend most of their time while executing on CPU.
Assuming that our goals align with the scenarios mentioned in the Why use threading section, let’s now discuss its types.
There are two different kinds of threads:
Kernel threads are implemented and managed by the kernel. The process context information and the threads are all managed by the kernel itself. Therefore, kernel threads are slower than user threads.
User threads are implemented and managed by users, i.e. creating or destroying thread, saving or restoring thread, scheduling thread, passing message or data to the thread and, the kernel is unaware of its existence. The kernel handles these threads as if they were single-threaded processes.
user threads and to manage them.Now the big question arises,
Python provides us with two modules to support threading.
_thread provides a method start_new_thread() that starts a new thread and returns its identifier.
Syntax
thread.start_new_thread(myFunction, args[, kwargs])This method takes 2 arguments, a function to be executed myFunction and a list of tuple arguments args. kwargs argument is an optional keyword arguments (Dictionary).
Let us consider the following program,
import _thread
import time
from time import ctime
def myFunction(myThread):
counter = 5
while counter > 0:
print(myThread + " " + ctime(time.time()))
counter -= 1
try:
_thread.start_new_thread(myFunction, ("Thread 1",))
_thread.start_new_thread(myFunction, ("Thread 2",))
except:
print("Thread could not be started")
while True:
passOutput:
Thread 1 Sat Sep 19 13:04:03 2020
Thread 2 Sat Sep 19 13:04:03 2020
Thread 1 Sat Sep 19 13:04:03 2020
Thread 1 Sat Sep 19 13:04:03 2020
Thread 1 Sat Sep 19 13:04:03 2020
Thread 1 Sat Sep 19 13:04:03 2020
Thread 2 Sat Sep 19 13:04:03 2020
Thread 2 Sat Sep 19 13:04:03 2020
Thread 2 Sat Sep 19 13:04:03 2020
Thread 2 Sat Sep 19 13:04:03 2020Thread 1 and Thread 2 both read the value of the variable counter, which is 5, at time 13:22:01.13:22:01.2 and 3 seconds, respectively.Thread 1 wakes up, decrements the value of counter by 1, and moves with the next iteration, which then executes the print statement and puts the thread to sleep again for 2 seconds.13:22:04, Thread 2 wakes up and decrements the value of counter and continues with the next iteration, which then executes the print statement and puts the thread to sleep again for 3 seconds.counter for both Thread 1 and Thread 2 is 0.Note: If we run this program multiple times, we will notice that the output sequence changes every time. It happens because the threads are not synchronized, and they run whenever they have the required resources.
_thread is an effective option for low-level threading. But Python’s new threading module is more powerful and supports high-level threading.
To create a new thread, we create an object of Thread class, which takes target as a parameter.
target: Name of the function to be executedargs: Optional parameter to pass argument tupleSyntax
threading.Thread(target=myFunction, args* = ())Consider the following example.
import threading
import time
from time import ctime
def myThread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
for i in range(0, 3):
print("Creating thread %d at %s" % (i, ctime(time.time())))
thread = threading.Thread(target=myThread, args=(i,))
print("Starting thread %d at %s" % (i, ctime(time.time())))
thread.start()threading module, we need to import it using import threadingthreading.Thread(target=myThread, args=(i,)) where we have passed i as an argument. Note that the target is myThread() function.start() method is used to start the execution of a thread.Output:
Creating thread 0 at Fri Sep 18 16:24:25 2020
Starting thread 0 at Fri Sep 18 16:24:25 2020
Thread 0: started at Fri Sep 18 16:24:25 2020
Creating thread 1 at Fri Sep 18 16:24:25 2020
Starting thread 1 at Fri Sep 18 16:24:25 2020
Thread 1: started at Fri Sep 18 16:24:25 2020
Creating thread 2 at Fri Sep 18 16:24:25 2020
Starting thread 2 at Fri Sep 18 16:24:25 2020
Thread 2: started at Fri Sep 18 16:24:25 2020
Thread 0: finished at Fri Sep 18 16:24:27 2020
Thread 2: finished at Fri Sep 18 16:24:27 2020
Thread 1: finished at Fri Sep 18 16:24:27 2020Now, What if we want the create a new thread only after the previous one has been completed or stopped? In that case, we use the join() method as below.
import threading
import time
from time import ctime
def myThread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
for i in range(0, 3):
print("Creating thread %d at %s" % (i, ctime(time.time())))
thread = threading.Thread(target=myThread, args=(i,))
print("Starting thread %d at %s" % (i, ctime(time.time())))
thread.start()
thread.join()Output:
Creating thread 0 at Fri Sep 18 16:37:26 2020
Starting thread 0 at Fri Sep 18 16:37:26 2020
Thread 0: started at Fri Sep 18 16:37:26 2020
Thread 0: finished at Fri Sep 18 16:37:28 2020
Creating thread 1 at Fri Sep 18 16:37:28 2020
Starting thread 1 at Fri Sep 18 16:37:28 2020
Thread 1: started at Fri Sep 18 16:37:28 2020
Thread 1: finished at Fri Sep 18 16:37:30 2020
Creating thread 2 at Fri Sep 18 16:37:30 2020
Starting thread 2 at Fri Sep 18 16:37:30 2020
Thread 2: started at Fri Sep 18 16:37:30 2020
Thread 2: finished at Fri Sep 18 16:37:32 2020join() pauses the main thread (for loop in this case) and wait for the running thread to complete.
Note that Thread 1 is created after Thread 0 has finished and Thread 2 is created after Thread 1 has finished.
We can also pass a timeout time in the join method. It is used to manually timeout the current thread and allows the next thread to execute. We can call the is_alive() method (checks whether a thread is still executing and returns boolean) after join() to check whether a timeout happened. If the thread is still alive, the join() times out.
import threading
import time
from time import ctime
def myThread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
for i in range(0, 3):
print("Creating thread %d at %s" % (i, ctime(time.time())))
thread = threading.Thread(target=myThread, args=(i,))
print("Starting thread %d at %s" % (i, ctime(time.time())))
thread.start()
thread.join(1)
print("Thread alive: ",thread.is_alive())Output:
Creating thread 0 at Fri Sep 18 16:51:42 2020
Starting thread 0 at Fri Sep 18 16:51:42 2020
Thread 0: started at Fri Sep 18 16:51:42 2020
Thread alive: True
Creating thread 1 at Fri Sep 18 16:51:43 2020
Starting thread 1 at Fri Sep 18 16:51:43 2020
Thread 1: started at Fri Sep 18 16:51:43 2020
Thread 0: finished at Fri Sep 18 16:51:44 2020
Thread alive: True
Creating thread 2 at Fri Sep 18 16:51:44 2020
Starting thread 2 at Fri Sep 18 16:51:44 2020
Thread 2: started at Fri Sep 18 16:51:44 2020
Thread 1: finished at Fri Sep 18 16:51:45 2020
Thread alive: True
Thread 2: finished at Fri Sep 18 16:51:46 2020The threading module also provides the following additional methods -
threading.active_Count(): It returns the number of active threads.threading.current_Thread(): It returns the current thread being executed.threading.enumerate(): It returns a list of active threads.The below example uses these additional methods.
import threading
import time
from time import ctime
def myThread(num):
print("Current thread: ", threading.current_thread().getName())
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
for i in range(0, 2):
thread = threading.Thread(target=myThread, args=(i,))
thread.start()
thread.join(1)
print("Number of active threads: ", threading.active_count())
print("Current thread: ", threading.current_thread().getName())
print("Active threads: ")
for j in threading.enumerate():
print(j.getName(), end=";")Output:
Current thread: Thread-1
Thread 0: started at Fri Sep 18 17:32:26 2020
Number of active threads: 2
Current thread: MainThread
Active threads:
MainThread;Thread-1;
Current thread: Thread-2
Thread 1: started at Fri Sep 18 17:32:27 2020
Thread 0: finished at Fri Sep 18 17:32:28 2020
Number of active threads: 2
Current thread: MainThread
Active threads:
MainThread;Thread-2;
Thread 1: finished at Fri Sep 18 17:32:29 2020A
daemonthread is a which runs in the background. It has low priority so that it doesn’t affect execution of other threads.
Daemons threads can be killed when all the non-daemon threads have been executed successfully. For instance,
import threading
import time
from time import ctime
def daemon_thread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(3)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
def non_daemon_thread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
thread1 = threading.Thread(target=daemon_thread, args=(1,), daemon=True)
thread2 = threading.Thread(target=non_daemon_thread, args=(2,))
thread1.start()
thread2.start()Output:
Thread 1: started at Fri Sep 18 17:51:47 2020
Thread 2: started at Fri Sep 18 17:51:47 2020
Thread 2: finished at Fri Sep 18 17:51:49 2020Here daemon_thread, Thread 1 is killed as soon as non_daemon_thread Thread 2 has finished.
But what if we want the daemon thread to finish as well? join() method comes to the rescue.
import threading
import time
from time import ctime
def daemon_thread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(3)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
def non_daemon_thread(num):
print("Thread %d: started at %s" % (num, ctime(time.time())))
time.sleep(2)
print("Thread %d: finished at %s" % (num, ctime(time.time())))
thread1 = threading.Thread(target=daemon_thread, args=(1,), daemon=True)
thread2 = threading.Thread(target=non_daemon_thread, args=(2,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()Output:
Thread 1: started at Fri Sep 18 18:05:53 2020
Thread 2: started at Fri Sep 18 18:05:53 2020
Thread 2: finished at Fri Sep 18 18:05:55 2020
Thread 1: finished at Fri Sep 18 18:05:56 2020Unlike earlier, Thread 1 has also finished.
Threading is an important concept in Python.
In this tutorial, we have learned the concept of threads and multithreading in Python using two modules, _thread, and threading. We also learned about different methods provided by these modules like start(), join(), is_alive(), current_Thread(), active_Thread, and enumerate. With this article, we should also have a clear understanding of when and when not to use threading.
Help us improve this content by editing this page on GitHub