Python mutex example

Introduction

I highly recommend that you check out the following article to get a quick idea about different terms used in operating systems with regard to concurrency, multitasking and threading. Back to our topic, In this short post, I am going to demonstrate the use of mutex in python. Let us get started.

Python mutex example

What on earth is a mutex ? In operating systems terminology, a mutex (derived from the words "mutual exclusion") is a synchronization primitive. I know that is not a cool definition for beginners so let us describe it using a real life example. Can we eat a cake before it is baked ? maybe but there is a chance we gonna mess things up. Usually, we wait until the food is cooked then we eat it. This analogy is not far from synchronization primitives (ex. mutex). In a modern operating system, typically, we have an application (process) with multiple threads of execution or subtasks. We want the threads to run simultaneously without interfering, otherwise; all sorts of nasty bugs happen. If one thread is computing a value and the other thread is waiting to consume that value, then proper synchronization is required. The famous example is a bank account shared between two threads. One thread deposits to the account and the other one withdraws from it. If these two threads are not synchronized then a race condition may happen and the end result people getting fired.

Python mutex vs lock

We should be very careful not to confuse terminology with programming language syntax. Lock is a generic term. It is a mechanism to implement mutual exclusion to protect shared resources between processes or threads. A lock and a mutex are often used interchangeably however in Python the mutex is a module that defines a class to allow mutual exclusion by acquiring and releasing a lock object. Please note that this class is deprecated (in Python 3.0) and should be avoided. We will see later an alternative Python syntax (threading.Lock) to implement mutual exclusion.

Python mutex example

Given two threads of execution where each one can run for a random amount of time. Since the threads are running simultaneously, there is no way we can control which one is going to run first and which one is going to run next. Also, we want the first thread to finish first and the second thread to wait until the first thread is done. Let us see how to ensure the order of execution using a mutex.

# Import the required modules
import threading, time, random

# First thread class. Note that we are inheriting
# from the Thread class
class thread_one(threading.Thread):
	# A thread has a run method that needs to
	# be implemented
	def run(self):
		print ("The first thread is now sleeping")
		time.sleep(random.randint(1, 5))
		print("First thread has finished")

# Second thread class
class thread_two(threading.Thread):
	def run(self):
		print ("The second thread is now sleeping")
		time.sleep(random.randint(1, 5))
		print("Second thread has finished")

# Define two threads
t1 = thread_one()
t2 = thread_two()

# Start threads
t1.start()
t2.start()

Since each thread sleeps a random amount of time, there is no guarantee that the first thread is going to finish first. If that happens, this leads to a race condition which may crash the application if the two threads happen to manipulate a shared resource or variable.

Python implement mutex

The solution is straightforward. If the second thread finishes before the first thread, it should wait. To do that, we lock the second thread just after it finishes and force it to wait. Once the first thread is done, we unlock or release the second thread. Here is the modified source code

import threading, time, random

# Python create mutex
my_mutex = threading.Lock()
class thread_one(threading.Thread):
	def run(self):
		# Python mutex global
		global my_mutex
		print ("The first thread is now sleeping")
		time.sleep(random.randint(1, 5))
		print("First thread is finished")
		# Python release mutex: once the first thread
		# is done, we release the lock
		my_mutex.release()

class thread_two(threading.Thread):
	def run(self):
		# Python mutex global
		global my_mutex
		print ("The second thread is now sleeping")
		time.sleep(random.randint(1, 5))
		# Python mutex acquire: second thread has to
		# to keep waiting until the lock is released
		my_mutex.acquire()
		print("Second thread is finished")

# Python mutex acquire: main thread is acquiring the lock 
my_mutex.acquire()
t1 = thread_one()
t2 = thread_two()
t1.start()
t2.start()

Please note the following:

  • Imagine there is a door at which the second thread has to wait. Just before we start the threads, we lock (using acquire method) the mutex (door) so that the second threads won’t enter. For example, if the second thread runs for one second then there is a chance it won’t wait for the first thread to finish (ex. first thread takes 5 seconds). That is why we lock the door before doing anything (Line 28).
  • We used the global keyword (Lines 8, 19) in the run method of each thread because our mutex was declared as a global variable and should be accessed from both threads.
  • After the first thread is done, we let the second thread exit the door by releasing the mutex (Line 14).
  • The second thread tries to acquire the lock after it finishes all the work (Line 24). Note that the print statement comes right after the acquire statement not before because as long as the thread is waiting, it has not finished yet.

Mutex vs semaphore

Semaphores are also used to synchronize threads of execution so what is the difference between a mutex and a semaphore ? Let us quickly mention what is a semaphore? and do the comparison. A semaphore is another high level synchronization primitive. It restricts the number of simultaneous threads to a shared resource up to a maximum number known as the semaphore count. When a thread requests to access a resource, the semaphore counter is decremented. When the thread finishes, the counter is incremented. In that sense, a mutex is nothing but a binary semaphore meaning there can be only one thread at a time acquiring the mutex. The toilet example when comparing mutex to semaphores is very popular. A mutex is like a key to a toilet. One person at a time can have the key and occupy the toilet. When finished, he gives the key to the next person waiting in line. On the other hand, a semaphore is like having a number of identical toilet keys for several available toilets. The number of keys represents the semaphore count. The count is decremented whenever people come in. When all toilets are occupied, the count becomes zero so no one can use any toilets. If someone leaves, the count is incremented and the next person in line can use that key and so on.

Summary

  • Mutex is a simple synchronization primitive that can be used to protect a shared resource in a multithreading operating system.
  • To create a mutex in Python, import the threading module and use the syntax: mutex = threading.Lock()
  • Use the acquire method to lock a mutex and prevent other threads from entering a block of code or accessing a shared variable.
  • Use the release method to free a locked resource and allow other threads to have access
  • A mutex can be thought of as a binary semaphore (count = 1).

References

This article is not the best place to explain synchronization primitives in details. The main goal was to demonstrate how to use a mutex in Python. For more information you can refer to the following:

That is it for today. Please leave a comment or ask questions in the comment section below. Thanks for reading.

Search Terms...
2 Comments

Leave a Reply