Thread and Mutex#

This page describes the usage of threads and mutex in python.

Mutex in Python can be imported and used with the following statement :

from threading import Lock

Thread in Python can be imported and used with the following statement :

from threading import Thread

Warning

Thread instances from threading are executed concurrently (only one CPU core is used for all threads). If you want to execute threads by multiple core, so in a parallelism way, you must use this import statement :

from multiprocessing import Process

However, this page focuses only on concurrent threads.

How to use Mutex#

from threading import Lock

# create the mutex
mutex = Lock()

# acquire the mutex before a critical section
mutex.acquire()
print("Hello World)
# release once the critical section is finished
mutex.release()

Warning

Pay attention to the deadlock situation :

Python Logo

source : https://www.sqlshack.com/understanding-the-deadlock-definition-in-sql-server/#

How to use Thread#

Two possibilities :

  1. Create a Thread object and set it a target function

from threading import Thread

# function that will be executed by the thread
def processData(data):
   print('Processing data: {}'.format(data))

# creating a few threads
for i in range(0, 10):
   t = Thread(target=processData, args=(i))
   # way to launch the thread
   t.start()
  1. Implement a class that inherits from Thread and overwrite both __init__() and run() methods

from threading import Thread

# the class Handler inherits from Thread
class Handler(Thread):

   def __init__(self, client, userdata, msg):
      Thread.__init__(self)

   # will be executed when method start() is called
   def run(self):
      print("Hello World)

# create and start thread
handler = Handler()
handler.start()

Using Mutex to prevent conflicting usage of resources by different Threads#

Using Mutex with one single thread does not make much sense usually. However, with several Threads working either concurrently or in parallel, it all makes sense.

The following code :

#!/usr/bin/env python
import threading

def processData(data):
   thread_id = threading.get_ident()
   print('\nProcessing data:', data, "ThreadId:", thread_id)

counter = 0
max_run = 10
while True:
   some_data = counter
   t = threading.Thread(target=processData, args=(some_data))
   t.start()
   counter = counter + 1
   if counter >= max_run:
      break

will produce this output :

Processing data: 0 ThreadId: 13592

Processing data: 1 ThreadId: 22840

Processing data:
Processing data: 3 ThreadId: 17724

Processing data:
Processing data:
Processing data: 2 ThreadId: 5228
5 ThreadId: 18468

Processing data:
Processing data: 4 ThreadId: 2008
8 ThreadId: 4240
7 ThreadId: 25236

Processing data: 6 ThreadId: 21216
9 ThreadId: 11256

This is because the instances of Thread are trying to use the print() function simultaneously.

With a Mutex used before accessing the print() function, which is a critical section in a multi-threaded program :

#!/usr/bin/env python
import threading
mutex = threading.Lock()

def processData(data, thread_safe):
   if thread_safe:
      mutex.acquire()
   try:
      thread_id = threading.get_ident()
      print('\nProcessing data:', data, "ThreadId:", thread_id)
   finally:
      if thread_safe:
          mutex.release()

counter = 0
max_run = 10
thread_safe = False
while True:
   some_data = counter
   t = threading.Thread(target=processData, args=(some_data, thread_safe))
   t.start()
   counter = counter + 1
   if counter >= max_run:
      break

the output now looks much better :

Processing data: 0 ThreadId: 24996

Processing data: 1 ThreadId: 2008

Processing data: 2 ThreadId: 2076

Processing data: 3 ThreadId: 22288

Processing data: 4 ThreadId: 18396

Processing data: 5 ThreadId: 21540

Processing data: 6 ThreadId: 21696

Processing data: 7 ThreadId: 22976

Processing data: 8 ThreadId: 1172

Processing data: 9 ThreadId: 3212

Python Thread Mutex