26 Multi Threading Study Material PDF
26 Multi Threading Study Material PDF
26 Multi Threading Study Material PDF
Complete
Python
In
Simple Way
1 https://www.youtube.com/durgasoftware
MULTI
THREADING
STUDY MATERIAL
2 https://www.youtube.com/durgasoftware
Multi Tasking:
Executing several tasks simultaneously is the concept of multitasking.
Note: Whether it is process based or thread based, the main advantage of multi tasking is
to improve performance of the system by reducing response time.
Note: Where ever a group of independent jobs are available, then it is highly
recommended to execute simultaneously instead of executing one by one.For such type of
cases we should go for Multi Threading.
Python provides one inbuilt module "threading" to provide support for developing
threads. Hence developing multi threaded Programs is very easy in python.
Every Python Program by default contains one thread which is nothing but
MainThread.
3 https://www.youtube.com/durgasoftware
Q) Program to print Name of Current executing Thread
1) import threading
2) print("Current Executing Thread:",threading.current_thread().getName())
Note: threading module contains function current_thread() which returns the current
executing Thread object. On this object if we call getName() method then we will get
current executing thread name.
If multiple threads present in our program, then we cannot expect execution order and
hence we cannot expect exact output for the multi threaded programs. B'z of this we
cannot provide exact output for the above program.It is varied from machine to machine
and run to run.
Note: Thread is a pre defined class present in threading module which can be used to
create our own Threads.
4 https://www.youtube.com/durgasoftware
1) from threading import *
2) class MyThread(Thread):
3) def run(self):
4) for i in range(10):
5) print("Child Thread-1")
6) t=MyThread()
7) t.start()
8) for i in range(10):
9) print("Main Thread-1")
5 https://www.youtube.com/durgasoftware
With Multi Threading
1) from threading import *
2) import time
3) def doubles(numbers):
4) for n in numbers:
5) time.sleep(1)
6) print("Double:",2*n)
7) def squares(numbers):
8) for n in numbers:
9) time.sleep(1)
10) print("Square:",n*n)
11)
12) numbers=[1,2,3,4,5,6]
13) begintime=time.time()
14) t1=Thread(target=doubles,args=(numbers,))
15) t2=Thread(target=squares,args=(numbers,))
16) t1.start()
17) t2.start()
18) t1.join()
19) t2.join()
20) print("The total time taken:",time.time()-begintime)
We can get and set name of thread by using the following Thread class methods.
t.getName() Returns Name of Thread
t.setName(newName) To set our own name
Note: Every Thread has implicit variable "name" to represent name of Thread.
Output
MainThread
Pawan Kalyan
Pawan Kalyan
6 https://www.youtube.com/durgasoftware
Thread Identification Number (ident):
For every thread internally a unique identification number is available. We can access this
id by using implicit variable "ident"
Output:
Child Thread
Main Thread Identification Number: 2492
Child Thread Identification Number: 2768
active_count():
This function returns the number of active threads currently running.
Output:
D:\python_classes>py test.py
The Number of active Threads: 1
ChildThread1 ...started
ChildThread2 ...started
ChildThread3 ...started
7 https://www.youtube.com/durgasoftware
The Number of active Threads: 4
ChildThread1 ...ended
ChildThread2 ...ended
ChildThread3 ...ended
The Number of active Threads: 1
enumerate() Function:
This function returns a list of all active threads currently running.
Output:
D:\python_classes>py test.py
ChildThread1 ...started
ChildThread2 ...started
ChildThread3 ...started
Thread Name: MainThread
Thread Name: ChildThread1
Thread Name: ChildThread2
Thread Name: ChildThread3
ChildThread1 ...ended
ChildThread2 ...ended
ChildThread3 ...ended
Thread Name: MainThread
8 https://www.youtube.com/durgasoftware
isAlive() Method:
isAlive() method checks whether a thread is still executing or not.
Output:
D:\python_classes>py test.py
ChildThread1 ...started
ChildThread2 ...started
ChildThread1 is Alive : True
ChildThread2 is Alive : True
ChildThread1 ...ended
ChildThread2 ...ended
ChildThread1 is Alive : False
ChildThread2 is Alive : False
join() Method:
If a thread wants to wait until completing some other thread then we should go for join()
method.
In the above example Main Thread waited until completing child thread. In this case
output is:
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
t.join(seconds)
In this case thread will wait only specified amount of time.
Output
Seetha Thread
Seetha Thread
Seetha Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Rama Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Seetha Thread
Daemon Threads:
The threads which are running in the background are called Daemon Threads.
The main objective of Daemon Threads is to provide support for Non Daemon
Threads( like main thread)
Eg: Garbage Collector
Whenever Main Thread runs with low memory, immediately PVM runs Garbage
Collector to destroy useless objects and to provide free memory,so that Main Thread
can continue its execution without having any memory problems.
We can check whether thread is Daemon or not by using t.isDaemon() method of
Thread class or by using daemon property.
11 https://www.youtube.com/durgasoftware
We can change Daemon nature by using setDaemon() method of Thread class.
t.setDaemon(True)
But we can use this method before starting of Thread.i.e once thread started,we
cannot change its Daemon nature,otherwise we will get
RuntimeException:cannot set daemon status of active thread.
Default Nature:
By default Main Thread is always non-daemon.But for the remaining threads Daemon
nature will be inherited from parent to child.i.e if the Parent Thread is Daemon then child
thread is also Daemon and if the Parent Thread is Non Daemon then ChildThread is also
Non Daemon.
Note: Main Thread is always Non-Daemon and we cannot change its Daemon Nature b'z
it is already started at the beginning only.
Whenever the last Non-Daemon Thread terminates automatically all Daemon Threads will
be terminated.
12 https://www.youtube.com/durgasoftware
12) print("End Of Main Thread")
In the above program if we comment Line-1 then both Main Thread and Child Threads are
Non Daemon and hence both will be executed until their completion.
In this case output is:
Lazy Thread
Lazy Thread
Lazy Thread
End Of Main Thread
Lazy Thread
Lazy Thread
Lazy Thread
Lazy Thread
Lazy Thread
Lazy Thread
Lazy Thread
If we are not commenting Line-1 then Main Thread is Non-Daemon and Child Thread is
Daemon. Hence whenever MainThread terminates automatically child thread will be
terminated. In this case output is
Lazy Thread
Lazy Thread
Lazy Thread
End of Main Thread
Synchronization:
If multiple threads are executing simultaneously then there may be a chance of data
inconsistency problems.
13 https://www.youtube.com/durgasoftware
Output
Good Evening:Good Evening:Yuvraj
Dhoni
Good Evening:Good Evening:Yuvraj
Dhoni
....
We are getting irregular output b'z both threads are executing simultaneously wish()
function.
To overcome this problem we should go for synchronization.
In synchronization the threads will be executed one by one so that we can overcome
data inconsistency problems.
Synchronization means at a time only one Thread
Note: To call release() method compulsory thread should be owner of that lock.i.e thread
should has the lock already,otherwise we will get Runtime Exception saying
RuntimeError: release unlocked lock.
14 https://www.youtube.com/durgasoftware
If we are commenting line-1 then we will get RuntimeError: release unlocked lock
In the above program at a time only one thread is allowed to execute wish() method and
hence we will get regular output.
Output
D:\python_classes>py test.py
Main Thread trying to acquire Lock
Main Thread trying to acquire Lock Again
--
In the above Program main thread will be blocked b'z it is trying to acquire the lock second
time.
15 https://www.youtube.com/durgasoftware
Note: To kill the blocking thread from windows command prompt we have to use
ctrl+break. Here ctrl+C won't work.
If the Thread calls recursive functions or nested access to resources,then the thread may
trying to acquire the same lock again and again,which may block our thread.
Hence Traditional Locking mechanism won't work for executing recursive functions.
To overcome this problem, we should go for RLock(Reentrant Lock). Reentrant means the
thread can acquire the same lock again and again.If the lock is held by other threads then
only the thread will be blocked.
Reentrant facility is available only for owner thread but not for other threads.
In this case Main Thread won't be Locked b'z thread can acquire the lock any number of
times.
This RLock keeps track of recursion level and hence for every acquire() call compulsory
release() call should be available. i.e the number of acquire() calls and release() calls
should be matched then only lock will be released.
Eg:
l=RLock()
l.acquire()
l.acquire()
l.release()
l.release()
Note:
1) Only owner thread can acquire the lock multiple times
2) The number of acquire() calls and release() calls should be matched.
16 https://www.youtube.com/durgasoftware
Demo Program for Synchronization by using RLock:
1) from threading import *
2) import time
3) l=RLock()
4) def factorial(n):
5) l.acquire()
6) if n==0:
7) result=1
8) else:
9) result=n*factorial(n-1)
10) l.release()
11) return result
12)
13) def results(n):
14) print("The Factorial of",n,"is:",factorial(n))
15)
16) t1=Thread(target=results,args=(5,))
17) t2=Thread(target=results,args=(9,))
18) t1.start()
19) t2.start()
Output:
The Factorial of 5 is: 120
The Factorial of 9 is: 362880
In the above program instead of RLock if we use normal Lock then the thread will be
blocked.
17 https://www.youtube.com/durgasoftware
Synchronization by using Semaphore:
☕ In the case of Lock and RLock, at a time only one thread is allowed to execute.
☕ Sometimes our requirement is at a time a particular number of threads are allowed to
access(like at a time 10 memebers are allowed to access database server,4 members
are allowed to access Network connection etc).To handle this requirement we cannot
use Lock and RLock concepts and we should go for Semaphore concept.
☕ Semaphore can be used to limit the access to the shared resources with limited
capacity.
☕ Semaphore is advanced Synchronization Mechanism.
☕ We can create Semaphore object as follows s = Semaphore(counter)
☕ Here counter represents the maximum number of threads are allowed to access
simultaneously. The default value of counter is 1.
☕ Whenever thread executes acquire() method,then the counter value will be
decremented by 1 and if thread executes release() method then the counter value will
be incremented by 1.
☕ i.e for every acquire() call counter value will be decremented and for every release()
call counter value will be incremented.
Case-1: s = Semaphore()
In this case counter value is 1 and at a time only one thread is allowed to access. It is
exactly same as Lock concept.
Case-2: s = Semaphore(3)
In this case Semaphore object can be accessed by 3 threads at a time.The remaining
threads have to wait until releasing the semaphore.
18 https://www.youtube.com/durgasoftware
17) t1.start()
18) t2.start()
19) t3.start()
20) t4.start()
21) t5.start()
In the above program at a time 2 threads are allowed to access semaphore and hence 2
threads are allowed to execute wish() function.
Bounded Semaphore:
Normal Semaphore is an unlimited semaphore which allows us to call release() method
any number of times to increment counter.The number of release() calls can exceed the
number of acquire() calls also.
☕ It is valid because in normal semaphore we can call release() any number of times.
☕ BounedSemaphore is exactly same as Semaphore except that the number of release()
calls should not exceed the number of acquire() calls,otherwise we will get
ValueError: Semaphore released too many times
19 https://www.youtube.com/durgasoftware
Note: To prevent simple programming mistakes, it is recommended to use
BoundedSemaphore over normal Semaphore.
Conclusion:
The main advantage of synchronization is we can overcome data inconsistency
problems.But the main disadvantage of synchronization is it increases waiting time of
threads and creates performance problems. Hence if there is no specific requirement then
it is not recommended to use synchronization.
Demo Program-1:
Output:
Consumer thread is waiting for updation
Producer thread producing items
Producer thread giving notification by setting event
Consumer thread got notification and consuming items
Demo Program-2:
In the above program driver thread has to wait until Trafficpolice thread sets event.ie until
giving GREEN signal.Once Traffic police thread sets event(giving GREEN signal),vehicles can
cross the signal. Once traffic police thread clears event (giving RED Signal)then the driver
thread has to wait.
Methods of Condition:
1) acquire() To acquire Condition object before producing or consuming items.i.e
thread acquiring internal lock.
2) release() To release Condition object after producing or consuming items. i.e thread
releases internal lock
22 https://www.youtube.com/durgasoftware
3) wait()|wait(time) To wait until getting Notification or time expired
Case Study:
The producing thread needs to acquire the Condition before producing item to the
resource and notifying the consumers.
#Producer Thread
...generate item..
condition.acquire()
...add item to the resource...
condition.notify()#signal that a new item is available(notifyAll())
condition.release()
The Consumer must acquire the Condition and then it can consume items from the
resource
#Consumer Thread
condition.acquire()
condition.wait()
consume item
condition.release()
Demo Program-1:
23 https://www.youtube.com/durgasoftware
16) c=Condition()
17) t1=Thread(target=consume,args=(c,))
18) t2=Thread(target=produce,args=(c,))
19) t1.start()
20) t2.start()
Output
Consumer waiting for updation
Producer Producing Items
Producer giving Notification
Consumer got notification & consuming the item
Demo Program-2:
24 https://www.youtube.com/durgasoftware
Output
Consumer waiting for updation
Producer Producing Item: 49
Producer giving Notification
Consumer consumed the item 49
.....
In the above program Consumer thread expecting updation and hence it is responsible to
call wait() method on Condition object.
Producer thread performing updation and hence it is responsible to call notify() or
notifyAll() on Condition object.
Producer Thread uses put() method to insert data in the queue. Internally this method has
logic to acquire the lock before inserting data into queue. After inserting data lock will be
released automatically.
put() method also checks whether the queue is full or not and if queue is full then the
Producer thread will entered in to waiting state by calling wait() method internally.
Consumer Thread uses get() method to remove and get data from the queue. Internally
this method has logic to acquire the lock before removing data from the queue.Once
removal completed then the lock will be released automatically.
If the queue is empty then consumer thread will entered into waiting state by calling
wait() method internally.Once queue updated with data then the thread will be notified
automatically.
Note: The Queue Module takes care of locking for us which is a great Advantage.
25 https://www.youtube.com/durgasoftware
1) from threading import *
2) import time
3) import random
4) import queue
5) def produce(q):
6) while True:
7) item=random.randint(1,100)
8) print("Producer Producing Item:",item)
9) q.put(item)
10) print("Producer giving Notification")
11) time.sleep(5)
12) def consume(q):
13) while True:
14) print("Consumer waiting for updation")
15) print("Consumer consumed the item:",q.get())
16) time.sleep(5)
17)
18) q=queue.Queue()
19) t1=Thread(target=consume,args=(q,))
20) t2=Thread(target=produce,args=(q,))
21) t1.start()
22) t2.start()
Output
Consumer waiting for updation
Producer Producing Item: 58
Producer giving Notification
Consumer consumed the item: 58
Types of Queues:
Python Supports 3 Types of Queues.
1) FIFO Queue:
q = queue.Queue()
This is Default Behaviour. In which order we put items in the queue, in the same order
the items will come out (FIFO-First In First Out).
1) import queue
2) q=queue.Queue()
3) q.put(10)
4) q.put(5)
5) q.put(20)
26 https://www.youtube.com/durgasoftware
6) q.put(15)
7) while not q.empty():
8) print(q.get(),end=' ')
Output: 10 5 20 15
2) LIFO Queue:
The removal will be happend in the reverse order of Insertion (Last In First Out)
1) import queue
2) q=queue.LifoQueue()
3) q.put(10)
4) q.put(5)
5) q.put(20)
6) q.put(15)
7) while not q.empty():
8) print(q.get(),end=' ')
Output: 15 20 5 10
3) Priority Queue:
The elements will be inserted based on some priority order.
1) import queue
2) q=queue.PriorityQueue()
3) q.put(10)
4) q.put(5)
5) q.put(20)
6) q.put(15)
7) while not q.empty():
8) print(q.get(),end=' ')
Output: 5 10 15 20
Eg 2: If the data is non-numeric, then we have to provide our data in the form of tuple.
(x,y)
x is priority
y is our element
1) import queue
2) q=queue.PriorityQueue()
3) q.put((1,"AAA"))
27 https://www.youtube.com/durgasoftware
4) q.put((3,"CCC"))
5) q.put((2,"BBB"))
6) q.put((4,"DDD"))
7) while not q.empty():
8) print(q.get()[1],end=' ')
l = threading.Lock()
l.acquire()
try:
perform required safe operations
finally:
l.release()
Demo Program:
1) from threading import *
2) import time
3) l=Lock()
4) def wish(name):
5) l.acquire()
6) try:
7) for i in range(10):
8) print("Good Evening:",end='')
9) time.sleep(2)
10) print(name)
11) finally:
12) l.release()
13)
14) t1=Thread(target=wish,args=("Dhoni",))
15) t2=Thread(target=wish,args=("Yuvraj",))
16) t3=Thread(target=wish,args=("Kohli",))
17) t1.start()
28 https://www.youtube.com/durgasoftware
18) t2.start()
19) t3.start()
Case-2:
It is highly recommended to acquire lock by using with statement. The main advantage
of with statement is the lock will be released automatically once control reaches end
of with block and we are not required to release explicitly.
This is exactly same as usage of with statement for files.
Demo Program:
1) from threading import *
2) import time
3) lock=Lock()
4) def wish(name):
5) with lock:
6) for i in range(10):
7) print("Good Evening:",end='')
8) time.sleep(2)
9) print(name)
10) t1=Thread(target=wish,args=("Dhoni",))
11) t2=Thread(target=wish,args=("Yuvraj",))
12) t3=Thread(target=wish,args=("Kohli",))
13) t1.start()
14) t2.start()
15) t3.start()
29 https://www.youtube.com/durgasoftware
Q) What is the Advantage of using with Statement to acquire a Lock in
Threading?
Lock will be released automatically once control reaches end of with block and we are not
required to release explicitly.
Note: We can use with statement in multithreading for the following cases:
1) Lock
2) RLock
3) Semaphore
4) Condition
30 https://www.youtube.com/durgasoftware