Skip to content

Commit 8f48826

Browse files
committed
added mutex lock
1 parent 38d6379 commit 8f48826

File tree

3 files changed

+296
-24
lines changed

3 files changed

+296
-24
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ py -3.11 -m pip install numpy
2626
[Python File](./same_python_file/peterson_solution.py)    /    [Python File Alternative](./same_python_file/peterson_solution_alternative.py)
2727

2828
### Lab - 2 == Mutex Lock (Protect Critical-Sections & Prevent Race-Conditions)
29-
[jupyter File](./poisson_distribution.ipynb) <br/>
30-
[Python File](./same_python_file/poisson_distribution.py)
29+
[jupyter File](./mutex_lock.ipynb) <br/>
30+
[Python File](./same_python_file/mutex_lock.py)
3131

3232
### Lab - 3 == Semaphore Solution in Process-Synchronization (Producer Consumer Problem)
3333
[jupyter File](./unimodal_multimodal_density_curves_normal_distribution.ipynb) <br/>

mutex_lock.ipynb

Lines changed: 250 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,268 @@
11
{
22
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"https://www.pythontutorial.net/python-concurrency/python-threading-lock/"
8+
]
9+
},
10+
{
11+
"cell_type": "markdown",
12+
"metadata": {},
13+
"source": [
14+
"illustrates a race condition: Output will vary 10 & 20 each time you run , depending race"
15+
]
16+
},
317
{
418
"cell_type": "code",
519
"execution_count": null,
620
"metadata": {},
721
"outputs": [],
822
"source": [
9-
"import threading\n",
23+
"from threading import Thread\n",
24+
"from time import sleep\n",
25+
"\n",
26+
"\n",
27+
"counter = 0\n",
28+
"\n",
29+
"# define a function that increases the value of the counter variable by a number:\n",
30+
"def increase(by):\n",
31+
" global counter\n",
1032
"\n",
11-
"# Initialize the mutex lock\n",
12-
"mutex = threading.Lock()\n",
33+
" local_counter = counter\n",
34+
" local_counter += by\n",
1335
"\n",
14-
"# Function to demonstrate using the mutex lock\n",
15-
"def protected_critical_section(name):\n",
16-
" # Acquire the mutex lock to protect the critical section\n",
17-
" mutex.acquire()\n",
18-
" try:\n",
19-
" # Critical section code here\n",
20-
" print(f\"{name} is in the critical section.\")\n",
21-
" finally:\n",
22-
" # Release the mutex lock after the critical section is done\n",
23-
" mutex.release()\n",
36+
" sleep(0.1)\n",
37+
"\n",
38+
" counter = local_counter\n",
39+
" print(f'counter={counter}\\n')\n",
40+
"\n",
41+
"\n",
42+
"# create two threads. \n",
43+
"# first thread increases the counter by 10 \n",
44+
"# second thread increases the counter by 20:\n",
45+
"t1 = Thread(target=increase, args=(10,))\n",
46+
"t2 = Thread(target=increase, args=(20,))\n",
47+
"\n",
48+
"# start the threads\n",
49+
"t1.start()\n",
50+
"t2.start()\n",
51+
"\n",
52+
"\n",
53+
"# wait for the threads to complete\n",
54+
"t1.join()\n",
55+
"t2.join()\n",
56+
"\n",
57+
"\n",
58+
"print(f'The final counter is {counter}')"
59+
]
60+
},
61+
{
62+
"cell_type": "markdown",
63+
"metadata": {},
64+
"source": [
65+
"#### Using a threading lock to prevent the race condition\n",
2466
"\n",
25-
"# Create two threads to demonstrate using the mutex lock\n",
26-
"thread1 = threading.Thread(target=protected_critical_section, args=(\"Thread 1\",))\n",
27-
"thread2 = threading.Thread(target=protected_critical_section, args=(\"Thread 2\",))\n",
67+
"In Python, you can use the `Lock` class from the `threading` module to create a lock object:\n",
68+
"\n",
69+
"First, create an instance the Lock class:\n",
70+
"\n",
71+
"```python\n",
72+
"lock = Lock()\n",
73+
"```\n",
74+
"\n",
75+
"By default, the lock is unlocked until a thread acquires it.\n",
76+
"\n",
77+
"Second, acquire a lock by calling the acquire() method:\n",
78+
"\n",
79+
"```python\n",
80+
"lock.acquire()\n",
81+
"```\n",
82+
"\n",
83+
"Third, release the lock once the thread completes changing the shared variable:\n",
84+
"\n",
85+
"```python\n",
86+
"lock.release()\n",
87+
"```"
88+
]
89+
},
90+
{
91+
"cell_type": "code",
92+
"execution_count": null,
93+
"metadata": {},
94+
"outputs": [],
95+
"source": [
96+
"from threading import Thread, Lock\n",
97+
"from time import sleep\n",
98+
"\n",
99+
"# Initialize the global counter variable\n",
100+
"counter = 0\n",
101+
"\n",
102+
"# Define the thread function to increase the counter by a given value using a lock\n",
103+
"def increase(by, lock):\n",
104+
" global counter\n",
105+
"\n",
106+
" # Acquire the lock to ensure exclusive access to the shared counter\n",
107+
" lock.acquire()\n",
108+
"\n",
109+
" # Create a local copy of the counter to perform the update\n",
110+
" local_counter = counter\n",
111+
" local_counter += by\n",
112+
"\n",
113+
" # Simulate some time-consuming work using sleep\n",
114+
" sleep(0.1)\n",
115+
"\n",
116+
" # Update the global counter with the new value\n",
117+
" counter = local_counter\n",
118+
" print(f'counter={counter}')\n",
119+
"\n",
120+
" # Release the lock to allow other threads to access the shared counter\n",
121+
" lock.release()\n",
122+
"\n",
123+
"# Create a Lock object to synchronize access to the shared counter\n",
124+
"lock = Lock()\n",
125+
"\n",
126+
"# Create two threads, each incrementing the counter by a different value\n",
127+
"t1 = Thread(target=increase, args=(10, lock))\n",
128+
"t2 = Thread(target=increase, args=(20, lock))\n",
28129
"\n",
29130
"# Start the threads\n",
30-
"thread1.start()\n",
31-
"thread2.start()\n",
131+
"t1.start()\n",
132+
"t2.start()\n",
133+
"\n",
134+
"# Wait for the threads to complete their execution\n",
135+
"t1.join()\n",
136+
"t2.join()\n",
137+
"\n",
138+
"# Print the final value of the counter\n",
139+
"print(f'The final counter is {counter}')\n"
140+
]
141+
},
142+
{
143+
"cell_type": "markdown",
144+
"metadata": {},
145+
"source": [
146+
"It’s easier to use the lock object with the with statement to acquire and release the lock within a block of code:"
147+
]
148+
},
149+
{
150+
"cell_type": "code",
151+
"execution_count": null,
152+
"metadata": {},
153+
"outputs": [],
154+
"source": [
155+
"from threading import Thread, Lock\n",
156+
"from time import sleep\n",
157+
"\n",
158+
"counter = 0\n",
159+
"\n",
160+
"def increase(by, lock):\n",
161+
" global counter\n",
162+
"\n",
163+
" # Use the 'with' statement to acquire and release the lock automatically\n",
164+
" with lock:\n",
165+
" # Create a local copy of the counter to perform the update\n",
166+
" local_counter = counter\n",
167+
" local_counter += by\n",
168+
"\n",
169+
" # Simulate some time-consuming work using sleep\n",
170+
" sleep(0.1)\n",
171+
"\n",
172+
" # Update the global counter with the new value\n",
173+
" counter = local_counter\n",
174+
" print(f'counter={counter}')\n",
175+
"\n",
176+
"lock = Lock()\n",
177+
"\n",
178+
"# create threads\n",
179+
"t1 = Thread(target=increase, args=(10, lock))\n",
180+
"t2 = Thread(target=increase, args=(20, lock))\n",
181+
"\n",
182+
"# start the threads\n",
183+
"t1.start()\n",
184+
"t2.start()\n",
185+
"\n",
186+
"# wait for the threads to complete\n",
187+
"t1.join()\n",
188+
"t2.join()\n",
189+
"\n",
190+
"# Print the final value of the counter\n",
191+
"print(f'The final counter is {counter}')\n"
192+
]
193+
},
194+
{
195+
"cell_type": "markdown",
196+
"metadata": {},
197+
"source": [
198+
"### Alternative"
199+
]
200+
},
201+
{
202+
"cell_type": "markdown",
203+
"metadata": {},
204+
"source": [
205+
"Alternative code follows a more structured and object-oriented approach by encapsulating the shared counter and lock within a class. This approach is more modular and reusable, allowing you to easily create multiple instances of the Counter class with their own separate counters and locks, each capable of independent synchronization"
206+
]
207+
},
208+
{
209+
"cell_type": "code",
210+
"execution_count": null,
211+
"metadata": {},
212+
"outputs": [],
213+
"source": [
214+
"from threading import Thread, Lock\n",
215+
"from time import sleep\n",
216+
"\n",
217+
"# Define a class to encapsulate the shared counter and lock\n",
218+
"class Counter:\n",
219+
" def __init__(self):\n",
220+
" # Initialize the counter value to 0\n",
221+
" self.value = 0\n",
222+
" # Create a lock object to synchronize access to the counter\n",
223+
" self.lock = Lock()\n",
224+
"\n",
225+
" # Method to increase the counter by a specified value\n",
226+
" def increase(self, by):\n",
227+
" # Acquire the lock to ensure exclusive access to the shared counter\n",
228+
" with self.lock:\n",
229+
" # Create a local variable to perform the update operation\n",
230+
" current_value = self.value\n",
231+
" # Increment the local variable by the specified value\n",
232+
" current_value += by\n",
233+
"\n",
234+
" # Simulate some time-consuming work using sleep\n",
235+
" sleep(0.1)\n",
236+
"\n",
237+
" # Update the shared counter with the new value\n",
238+
" self.value = current_value\n",
239+
" # Print the updated value of the counter\n",
240+
" print(f'counter={self.value}')\n",
241+
"\n",
242+
"# Main function\n",
243+
"def main():\n",
244+
" # Create an instance of the Counter class to manage the shared counter\n",
245+
" counter = Counter()\n",
246+
"\n",
247+
" # Create two threads with different increment values\n",
248+
" t1 = Thread(target=counter.increase, args=(10, ))\n",
249+
" t2 = Thread(target=counter.increase, args=(20, ))\n",
250+
"\n",
251+
" # Start the threads\n",
252+
" t1.start()\n",
253+
" t2.start()\n",
254+
"\n",
255+
" # Wait for the threads to complete their work\n",
256+
" t1.join()\n",
257+
" t2.join()\n",
32258
"\n",
33-
"# Wait for both threads to finish\n",
34-
"thread1.join()\n",
35-
"thread2.join()\n",
259+
" # Print the final value of the counter after both threads have finished\n",
260+
" print(f'The final counter is {counter.value}')\n",
36261
"\n",
37-
"print(\"All threads finished.\")\n"
262+
"# Check if the script is run directly (not imported as a module)\n",
263+
"if __name__ == '__main__':\n",
264+
" # Call the main function to start the threads and perform the synchronization\n",
265+
" main()\n"
38266
]
39267
}
40268
],

same_python_file/mutex_lock.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from threading import Thread, Lock
2+
from time import sleep
3+
4+
# Initialize the global counter variable
5+
counter = 0
6+
7+
# Define the thread function to increase the counter by a given value using a lock
8+
def increase(by, lock):
9+
global counter
10+
11+
# Acquire the lock to ensure exclusive access to the shared counter
12+
lock.acquire()
13+
14+
# Create a local copy of the counter to perform the update
15+
local_counter = counter
16+
local_counter += by
17+
18+
# Simulate some time-consuming work using sleep
19+
sleep(0.1)
20+
21+
# Update the global counter with the new value
22+
counter = local_counter
23+
print(f'counter={counter}')
24+
25+
# Release the lock to allow other threads to access the shared counter
26+
lock.release()
27+
28+
# Create a Lock object to synchronize access to the shared counter
29+
lock = Lock()
30+
31+
# Create two threads, each incrementing the counter by a different value
32+
t1 = Thread(target=increase, args=(10, lock))
33+
t2 = Thread(target=increase, args=(20, lock))
34+
35+
# Start the threads
36+
t1.start()
37+
t2.start()
38+
39+
# Wait for the threads to complete their execution
40+
t1.join()
41+
t2.join()
42+
43+
# Print the final value of the counter
44+
print(f'The final counter is {counter}')

0 commit comments

Comments
 (0)