Overview:
Threads are light-weight executable entities within a process. A thread can not exist without a process. Those who are familiar with multithreading can skip the introduction and can navigate directly to working with multiple threads in Python. A quick introduction of processes, threads and their use-cases including how to create threads in a Python program is given here.
Processes:
A user working on a computer system can execute one or more processes. A process is the basic unit of execution in an operating system. An operating system decides and schedules which process has to run at any point in time on a given a CPU core. The operating system can execute a process for certain time period and can switch to another process. The process switching keeps on happening as long as the operating system runs, bringing many processes to life and collecting back the resources like physical memory as they are terminated.
Need for Threads:
An operating system maintains several resources for a process which include physical memory, file handles, network sockets and so on. Processes alone are not sufficient when the need arises for a process to work on several tasks at the same time. The operating system has to provide a facility to support executing multiple tasks of a process at the same time. This introduces threads to us.
What are Threads:
Threads are light-weights counterparts that reside inside a process. Threads are known to the operating system. The operating system handles Processes. It also handles threads within the processes.
All the threads within a process share the same address space. Only the stack and the few other piece of information like the previous stack pointer are private for a thread. It is really easy for an operating system to toss around threads rather than processes as they are light-weight.
Threads have a purpose - to represent a parallel or concurrent task within a process. Threads have several advantages over processes. Threads provide parallelism when multiple cores are available and they provide concurrency when a single core is present.
Multithreading use cases:
-
Word-Processor:
A word-processor can be active in editing mode while saving the document and sending an e-mail. All can happen simultaneously. While saving the document and sending it over email can be handled by the threads doing external I/O, the word processor can continue in edit mode which is handled by the UI thread. - Reader/Writer:
Many reader threads and writer threads spawned by a process can read from a file and write to another file. The number of readers and writers can be configured as per the number of hard-disk controllers.
Parallelism through multithreading:
When multiple threads are spawned by a process each CPU core can be assigned a thread by the operating system. Without the overhead involved in multiple processes and the inter-process communication, the threads can complete the given tasks parallelly. While tasks are completed parallelly the time taken can be reduced significantly if there are no inter-dependencies between them.
Concurrency:
In case of the word-processor use case, if there is only one CPU core available, multiple threads can still be executed by switching between the threads. Each threads will be given a number of time-slices by the operating system. Threads are made to yield control to other threads by making them go to wait state by the operating system scheduler. The whole execution of multiple threads using a single CPU gives the illusion of executing them parallelly while only one thread makes progress at a time.
Threading in Python:
In Python, the class Thread abstracts the concept of thread. The thread class have functions, that will be called by the runtime while executing a Python program. The statements inside the run() method constitutes a Thread.
From a Python program a thread cane be started in two ways:
- Any callable e.g., a function, that constitutes the thread can be specified as the target parameter of the Thread constructor. The start() method can be invoked on the Thread instance to start the thread.
- A thread can be created by subclassing the Thread class and overriding the run() method. Once the subclass is instantiated, the start() method has to be called on this object to execute the thread.
Create a Python thread with a callable object:
When creating a thread with a callable object, the function that forms the callable object, becomes the thread body. The python statements present inside that function forms the thread.
Once a thread object is created using a callable object, the thread can be started by calling the start() method on the object.
e.g.,
primeNumberThread = Thread(target=ProducePrimeNumbers)
primeNumberThread.start()
In the above example ProducePrimeNumbers is a Python function i.e., a callable object, passed as a target parameter of the Thread constructor.
Example: A multithreaded Python program to print prime numbers
from threading import Thread
#Callable function forming the thread body for the Prime Number Producer def ProducePrimeNumbers(): print("Prime number thread started") #2 is a prime number print(2)
for PrimeNumberCandidate in range (2,20):
IsPrime = False
for divisor in range(2,PrimeNumberCandidate): #Skip the Composite Number if PrimeNumberCandidate%divisor == 0: IsPrime = False; break else: #Mark the Prime Number IsPrime = True;
#Print the Prime Number if IsPrime == True: print(PrimeNumberCandidate)
print("Prime number thread exiting")
print("Main thread started") primeNumberThread = Thread(target=ProducePrimeNumbers)
#Start the prime number thread primeNumberThread.start()
#Let the Main thread wait for the prime thread to complete primeNumberThread.join() print("Main thread resumed") print("Main thread exiting")
|
Create a Python thread by overriding the run() method:
from threading import Thread
# A thread class to produce Fibonacci Numbers class FibonacciThread(Thread): def __init__(self): Thread.__init__(self)
#override the run method to define thread body def run(self): print("Fibonacci Thread Started") firstTerm = 0 secondTerm = 1 nextTerm = 0
for i in range(0,20): if ( i <= 1 ): nextTerm = i else: nextTerm = firstTerm + secondTerm firstTerm = secondTerm secondTerm = nextTerm
print(nextTerm)
print("Fibonacci Thread Ending")
print("Main Thread Started") fiboThread = FibonacciThread() fiboThread.start()
print("Main Thread Started to wait for the Fibonacci Thread to complete") fiboThread.join() print("Main Thread Resumed") print("Main Thread Ending") |