Enable Dark Mode!
threading-mutex-in-python.jpg
By: Chethana Ramachandran

Threading Mutex in Python

Technical

In Python, a thread refers to a separate flow of control within a program that can run concurrently with other threads. Threads are a fundamental part of multithreading, which is a way to achieve concurrency and parallelism in a Python program.

A mutex lock is often implemented in Python using threading.Lock() class from the threading module. A mutex lock is a synchronization primitive, a tool used to manage access to shared resources in a multithreaded program. The purpose of a mutex lock is to prevent multiple threads from accessing shared resources simultaneously, avoiding potential conflicts and ensuring data consistency.

To implement a mutex in a Python program, you need to use the Lock class from the threading module. The Lock class provides a way to create a mutex lock, and threads can acquire and release this lock to control access to critical sections of code. Importing the necessary modules is a prerequisite for implementing mutexes in Python, and this is done by importing Lock and Thread from the threading module.

from threading import Thread
from threading import Lock

Threads can create a lock instance, acquire it before entering a critical section, and release it once the critical section has been accessed.

Syntax:

# create the lock
mutex_lock = Lock()
# acquire the lock
mutex_lock.acquire()
# release the lock
mutex_lock.release()

The lock is exclusive to one thread at a time, and if a thread fails to release a previously acquired lock, it won't be able to acquire it again. When a thread seeks to acquire the lock, it will be in a blocked state until the lock is available, for example, if another thread currently holds the lock and subsequently releases it.

Attempting to acquire the lock without blocking is possible by setting the "blocking" argument to False. If the lock cannot be acquired, the function will return a value of False.

# acquire the lock without blocking
mutex_lock.acquire(blocking=false)

Another option is to try acquiring the lock with a timeout, which involves waiting for a specified number of seconds to acquire the lock before giving up. If the lock cannot be acquired within the set time frame, the function will return a value of False.

# acquire the lock with a timeout
mutex_lock.acquire(timeout=10)

The lock can be utilized through the context manager protocol using the with statement. This enables the critical section to be encapsulated within the block of the lock usage, ensuring automatic release of the lock once the block is executed.

# create the lock
mutex_lock = Lock()
# acquire the lock
with mutex_lock:
    # ...

Using the lock with the context manager protocol is the recommended approach as it delineates the start and end of the protected code. Moreover, it guarantees that the lock is released consistently, even in the presence of exceptions or errors within the critical section. Additionally, we can check whether the lock is currently held by a thread using the locked() function.

# check if the lock is currently acquired or not
if mutex_lock.locked():
    # ...

Example

import threading
import time
shared_counter = 0
# Mutex
mutex = threading.Lock()
# Function that will be executed by multiple threads
def increment_counter():
    global shared_counter
    # Acquire the mutex before modifying the shared counter
    mutex.acquire()
    try:
        # Critical section: access and modify the shared counter
        shared_counter += 1
        print(f"Thread {threading.current_thread().name} incremented counter to {shared_counter}")
    finally:
        # Release the mutex to allow other threads to acquire it
        mutex.release()
# Create multiple threads
threads = []
for i in range(3):
    thread = threading.Thread(target=increment_counter, name=f"Thread-{i}")
    threads.append(thread)
# Start the threads
for thread in threads:
    thread.start()
# Wait for all threads to finish
for thread in threads:
    thread.join()
print("Final value of shared counter:", shared_counter)

Output

Thread Thread-0 incremented counter to 1
Thread Thread-1 incremented counter to 2
Thread Thread-2 incremented counter to 3
Final value of shared counter: 3

In Odoo, we can define the mutex lock in the __init__ function

def __init__(self):
   self._lock = threading.Lock()
@http.route('/Customer_create', type='json', auth='public', method=['POST'], csrf=False)
def create_customer(self, *args, **kwargs):
   try:
       if request.httprequest.method == 'POST':
           self._lock.acquire()
           result = request.jsonrequest.get('Email')
           try:
               tr = request.env['res.partner'].sudo().search(
                   [('email', '=', result)])
               if tr:
                   _logger.error(f"{tr.id} customer existing")
                   return {"Status": "Error",
                           "Message": "Customer Already Exists"}
           except Exception as e:
               _logger.info(str(e))
           finally:
               self._lock.release()

Mutexes are essential for preventing race conditions and maintaining data integrity in concurrent programs. When multiple threads are involved, proper synchronization is crucial to avoid conflicts and unexpected behavior. If you want ti know more about Python and basic data structure in Python please refer to our previous blogs.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message