Python — Threading In Python3 (3)

So in my last article, we introduce “Race Condition” in Python3 and how to create a “Race Condition” situation. You can review it at: Python — Threading In Python3 (2)

There are few different ways of solving race condition. But the idea behind all of them is to allow only one thread modify shared the resource at a time. We will focus on Lock in this article.

The idea here is to implement a “lock” to the resources and to allow only one thread at time into the read/update section of the code. In Python3 threading, the basic functions to do this is .acquire() and .release() . .acquire() is called to get the lock and if the lock is already being given, the requiring thread will wait until it is released. In some situation, this could result a deadlock situation, if one thread for some reason never returns the lock, your thread will be stuck. To avoid deadlock , always use the with context manager.

Now back to our program, let’s remove the comments and enable the lock:

import concurrent.futures
import threading
import time


class ShoppingCart:
def __init__(self):
self.items = 0
self._lock = threading.Lock()

def update(self, name):
print(f'Customer-{name}: starting update')
with self._lock:
print(f'Customer-{name} has lock')
local_cart = self.items
local_cart += 1
time.sleep(1)
self.items = local_cart
print(f'Customer-{name} releases lock')
print(f'Customer-{name}: finishing update')


def main():
print(f'Main: Before starting shopping')
shopping_cart = ShoppingCart()
print(f'Start shopping, the cart is {shopping_cart.items}')
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as customer:
customer.map(shopping_cart.update, range(2))
print(f'Finish shopping, the cart is {shopping_cart.items}')
print(f'Main: After finishing shopping')


if __name__ == '__main__':
main()

Run it:

Main: Before starting shopping
Start shopping, the cart is 0
Customer-0: starting update
Customer-0 has lock
Customer-1: starting update
Customer-0 releases lock
Customer-0: finishing update
Customer-1 has lock
Customer-1 releases lock
Customer-1: finishing update
Finish shopping, the cart is 2
Main: After finishing shopping

Since we use the with context manager: with self._lock , the lock will get initialized in unlocked state and locked/released automatically. Use context manager is always recommended, to avoid deadlock

Just for demonstration purpose, you can generate a deadlock by using the following code snippet:

import threading
# Initialize a lock
t1 = threading.Lock()
print("Before the first lock acquire")
t1.acquire()
print("Before the second lock acquire")
t1.acquire()
print("Acquired lock twice")

Statement print("Acquired lock twice”) will never print.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store