IKH

Thread Synchronization

  • If multiple threads are executing simultaneously then there may be a chance of data inconsistency problems.

Example

Python
from threading import *
import time
def wish(name):
  for i in range(10):
    print("Good Evening:",end='')
    time.sleep(2)
    print(name)
t1=Thread(target=wish,args=("Mahi",))
t2=Thread(target=wish,args=("Chiku",))
t1.start()
t2.start()

Output

PowerShell
Good Evening:Good Evening:Mahi
Good Evening:Chiku
Good Evening:Mahi
Chiku
Good Evening:Good Evening:Mahi
Good Evening:Chiku
Good Evening:Mahi
Chiku
Good Evening:Good Evening:Mahi
Chiku
  • We are getting irregular output because 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.
  • The main application areas of synchronization are:
    • Online reservation system.
    • Online funds transfer.
  • In python, we can implement synchronization by using the following:
    • Lock
    • RLock
    • Semaphore

Synchronization by using Lock

  • Locks are the most fundamental synchronization mechanism provided by threading module.

Syntax

Python
lockObject = Lock()
  • The Lock object can be hold by only one thread at a time. If any other thread required the same lock then it will wait until thread releases lock.
  • Thread can acquire the lock by using acquire() method.
  • Thread can release the lock by using release() method.

Note

  • To call release() method compulsory thread should has the lock already, otherwise we will get runtime exception saying ‘release unlocked lock’.

Example

Python
from threading import *
l=Lock()
#l.acquire()
l.release()

Output

PowerShell
RuntimeError: release unlocked lock
  • If we are uncommenting line 3 then we will get no error.

Example

Python
from threading import *
import time
l=Lock()
def wish(name):
  l.acquire()
  for i in range(5):
    print("Good Evening:",end='')
    time.sleep(2)
    print(name)
  l.release()
t1=Thread(target=wish,args=("Mahi",))
t2=Thread(target=wish,args=("Chiku",))
t1.start()
t2.start()
  • In the above program at a time only one thread is allowed to execute wish() method and hence we will get regular output.

Output

PowerShell
Good Evening:Mahi
Good Evening:Mahi
Good Evening:Mahi
Good Evening:Mahi
Good Evening:Mahi
Good Evening:Chiku
Good Evening:Chiku
Good Evening:Chiku
Good Evening:Chiku
Good Evening:Chiku

Problem with Lock

  • The standard Lock object does not care which thread is currently holding that lock. If the lock is held and any thread attempts to acquire lock, then it will be blocked, even the same thread is already holding that lock.

Example

Python
from threading import *
l=Lock()
print("Main Thread trying to acquire Lock")
l.acquire()
print("Main Thread trying to acquire Lock Again")
l.acquire()

Output

PowerShell
Main Thread trying to acquire Lock
Main Thread trying to acquire Lock Again
  • In the above program main thread will be blocked because it is trying to acquire the lock second time.

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).

Synchronization by using RLock

  • 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.

Example

Python
from threading import * 
l=RLock() 
print("Main Thread trying to acquire Lock") 
l.acquire() 
print("Main Thread trying to acquire Lock Again") 
l.acquire()
  • In this case main thread won’t be locked because thread can acquire the lock any number of times.

Output

PowerShell
Main Thread trying to acquire Lock
Main Thread trying to acquire Lock Again
  • 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.

Example

Python
from threading import * 
l=RLock()
l.acquire()
l.acquire()
l.release()
l.release()
print("After 2 release() calls only the Lock will be released.")

Output

PowerShell
After 2 release() calls only the Lock will be released.

Note

  • Only owner thread can acquire the lock multiple times.

Example

Python
from threading import *
import time
l=RLock()
def factorial(n):
  l.acquire()
  if n==0:
    result=1
  else:
    result=n*factorial(n-1)
  l.release()
  return result
def results(n):
  print("The Factorial of",n,"is:",factorial(n))
t1=Thread(target=results,args=(5,))
t2=Thread(target=results,args=(9,))
t1.start()
t2.start()

Output

PowerShell
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.

Difference between Lock and RLock

LockRLock
Lock object can be acquired by only one thread at a time. Even owner thread also cannot acquire multiple times.RLock object can be acquired by only one thread at a time, but owner thread can acquire same lock object multiple times.
Not suitable to execute recursive functions and nested access calls.Best suitable to execute recursive functions and nested access calls.
Lock object will takes care only locked or unlocked and it never takes care about owner thread and recursion level.RLock object will takes care whether Locked or unlocked and owner thread information, recursion level.

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. 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.

Syntax

Python
counter = 10 # Some int value
semaphoreObject = Semaphore(counter)
  • Here counter represents the maximum number of threads are allowed to access simultaneously. The default value of counter is 1 i.e. at a time only one thread is allowed to access. It is exactly same as Lock concept.
  • 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.

Example

Python
from threading import *
import time
s=Semaphore(2)
def wish(name):
  s.acquire()
  for i in range(2):
    print("Good Evening:",end='')
    time.sleep(2)
    print(name)
  s.release()
t1=Thread(target=wish,args=("Dhoni",))
t2=Thread(target=wish,args=("Yuvraj",))
t3=Thread(target=wish,args=("Kohli",))
t1.start()
t2.start()
t3.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.

Output

PowerShell
Good Evening:Good Evening:Dhoni
Good Evening:Yuvraj
Good Evening:Dhoni
Yuvraj
Good Evening:Kohli
Good Evening:Kohli
  • 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.

Example

Python
from threading import *
s=Semaphore(2)
s.acquire()
s.acquire()
s.release()
s.release()
s.release()
s.release()
print("End")

Output

Python
End
  • It is valid because in normal semaphore we can call release() any number of times and increase the counter value at run time.

Bounded Semaphore

  • It is exactly same as Semaphore except that the number of release() calls should not exceed the number of acquire() calls, otherwise we will get value error.

Example

Python
from threading import *
s=BoundedSemaphore(2)
s.acquire()
s.acquire()
s.release()
s.release()
s.release()
s.release()
print("End")

Output

PowerShell
ValueError: Semaphore released too many times

Note

  • To prevent simple programming mistakes, it is recommended to use BoundedSemaphore over normal Semaphore.
  • 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.

Ungraded Questions

Get ready for an exhilarating evaluation of your understanding! Brace yourself as we dive into the upcoming assessment. Your active participation is key, so make sure to attend and demonstrate your knowledge. Let’s embark on this exciting learning journey together!


Name
Email
Phone

Report an error