Python | How to block critical sections

Code # 1:

import threading

 

class counter_share:

"" "

multiple streams can share.

"" "

  def __ init __ ( self , initial_key = 0 ):

self ._ key < code class = "keyword"> = initial_key

self ._ key_lock = threading.Lock ()

 

def incr ( self , delta = 1 ):

with self ._ key_lock:

# Increase counter with padlock

self ._ key + =   delta

 

def decr ( self , delta = 1 ):

with self ._ key_lock:

# Decrease the counter with a lock

self ._ key - = delta

Using the with statement in conjunction with blocking provides mutual exclusion. The exception is that only one thread (with the with statement) is allowed to execute a statement block at a time. 
A pending statement lock is obtained and released when control flow exits the block indented. Thread scheduling is inherently non-deterministic. This can lead to accidentally corrupted data and a “race condition” because locks cannot be used. Therefore, when multiple threads are accessing a shared mutable state, you should always use locks to avoid this.

In older Python code, there are often locks that are explicitly acquired and released.

Code # 2: Code Option 1

import threading

 

class counter_share:

# multiple threads can share counter objects

def __ init __ ( self , initial_key = 0 ):

  self ._key = initial_key

self ._ key_lock = threading.Lock ()

 

def incr ( self , delta = 1 ):

# Increase counter with padlock

self ._ key_lock.acquire ()

  s elf ._ key + = delta

  self . _key_lock.release ()

 

def decr ( self , delta = 1 ):

# Decrease the counter with a padlock

self ._ key_lock.acquire ()

  self ._ key - = d elta

self ._ key_lock.release ()

  • In situations where the release () is not called or an exception is not thrown when a lock is held, the with statement is much less error prone.
  • Each thread in a program is not allowed to acquire one lock at a time , this can potentially avoid deadlocks. Implement more advanced deadlock avoidance if this is not possible.
  • Synchronization primitives such as RLock and Semaphore objects are in the thread library.

In addition to just blocking a module, some other special tasks are solved:

  • RLock or a re-lockable lock object — it is a lock that can be acquired multiple times by the same thread.
  • It implements the code primarily through a locking or synchronization construct known as a "monitor". Only one thread can use the entire function or methods of the class while the lock is held, with this type of lock.

Code # 3: Implementing the SharedCounter class.

import threading

  

class counter_share:

# multiple threads can share counter objects

  _ lock = threading.RLock ()

 

def __init __ ( self , initial_key = 0 ):

self ._ key = initial_key

 

def incr ( self , delta = 1 ):

# Increase counter with padlock

with SharedCounter._lock:

self ._ key + =   delta

 

def decr ( self , delta = 1 ):

# Decrease the counter with a padlock

with SharedCounter._lock:

self . incr ( - delta)

  • Locking is meant to synchronize class methods, even though the lock is associated with mutable state each instance.
  • This version of the code has only one class-level lock shared by for all instances of a class.
  • Only one thread can use class methods at a time.
  • methods can call other methods that also use blocking if they already have locks. (For example, the decr () method).
  • If there are a large number of counters, this is much more memory efficient. However, this can lead to more lock conflicts in programs that use a large number of threads and frequently update the counter.

Semaphore element — this is a coarse synchronization dependent on a mutual counter. The counter increases when finished with a square. If the probability that the counter is zero, progress will be hindered until the counter is incremented by one more line. If the probability that the counter is nonzero, the explanation decreases the number and the line can continue. 
Instead of just blocking, semaphore elements are becoming more and more valuable to applications, including moving between lines or throttling. Although a semaphore can be used in a manner similar to a standard lock, the added versatility of use negatively affects execution.

Code # 4: Use a semaphore to limit the amount of parallelism in a piece of code.

from threading import Semaphore

import urllib.request

 
# maximum five threads can run concurrently

_ fetch_url_sema = Semaphore ( 5 )

 

 def fetch_url (url):

with _fetch_url_sema:

return urllib. request.urlopen (url)