Skip to content

Commit 7d14b80

Browse files
authored
Merge branch 'master' into python-variables-update
2 parents df1e22d + 754c56e commit 7d14b80

File tree

14 files changed

+403
-20
lines changed

14 files changed

+403
-20
lines changed

python-closure/roots.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def root_calculator(number):
88
square_root = make_root_calculator(2, 4)
99
print(square_root(42))
1010

11-
cubic_root = make_root_calculator(3, 2)
11+
cubic_root = make_root_calculator(3)
1212
print(cubic_root(42))
1313

1414

@@ -24,5 +24,5 @@ def __call__(self, number):
2424
square_root = RootCalculator(2, 4)
2525
print(square_root(42))
2626

27-
cubic_root = RootCalculator(3, 2)
27+
cubic_root = RootCalculator(3)
2828
print(cubic_root(42))

thread-safety-locks/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Python Thread Safety: Using a Lock and Other Techniques
2+
3+
This folder contains code examples from the Real Python tutorial [Python Thread Safety: Using a Lock and Other Techniques](https://realpython.com/python-thread-lock/).
4+
5+
## About the Author
6+
7+
Adarsh Divakaran - Website: https://adarshd.dev/
8+
9+
## License
10+
11+
Distributed under the MIT license. See ``LICENSE`` for more information.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import random
2+
import threading
3+
import time
4+
from concurrent.futures import ThreadPoolExecutor
5+
6+
teller_barrier = threading.Barrier(3)
7+
8+
9+
def now():
10+
return time.strftime("%H:%M:%S")
11+
12+
13+
def prepare_for_work(name):
14+
print(f"{now()}: {name} is preparing their counter.")
15+
16+
# Simulate the delay to prepare the counter
17+
time.sleep(random.randint(1, 3))
18+
print(f"{now()}: {name} has finished preparing.")
19+
20+
# Wait for all tellers to finish preparing
21+
teller_barrier.wait()
22+
print(f"{now()}: {name} is now ready to serve customers.")
23+
24+
25+
tellers = ["Teller 1", "Teller 2", "Teller 3"]
26+
27+
with ThreadPoolExecutor(max_workers=3) as executor:
28+
for teller_name in tellers:
29+
executor.submit(prepare_for_work, teller_name)
30+
31+
print(f"{now()}: All tellers are ready to serve customers.")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import random
2+
import threading
3+
import time
4+
from concurrent.futures import ThreadPoolExecutor
5+
6+
customer_available_condition = threading.Condition()
7+
8+
# Customers waiting to be served by the Teller
9+
customer_queue = []
10+
11+
12+
def now():
13+
return time.strftime("%H:%M:%S")
14+
15+
16+
def serve_customers():
17+
while True:
18+
with customer_available_condition:
19+
# Wait for a customer to arrive
20+
while not customer_queue:
21+
print(f"{now()}: Teller is waiting for a customer.")
22+
customer_available_condition.wait()
23+
24+
# Serve the customer
25+
customer = customer_queue.pop(0)
26+
print(f"{now()}: Teller is serving {customer}.")
27+
28+
# Simulate the time taken to serve the customer
29+
time.sleep(random.randint(1, 5))
30+
print(f"{now()}: Teller has finished serving {customer}.")
31+
32+
33+
def add_customer_to_queue(name):
34+
with customer_available_condition:
35+
print(f"{now()}: {name} has arrived at the bank.")
36+
customer_queue.append(name)
37+
38+
customer_available_condition.notify()
39+
40+
41+
customer_names = [
42+
"Customer 1",
43+
"Customer 2",
44+
"Customer 3",
45+
"Customer 4",
46+
"Customer 5",
47+
]
48+
49+
with ThreadPoolExecutor(max_workers=6) as executor:
50+
teller_thread = executor.submit(serve_customers)
51+
for name in customer_names:
52+
# Simulate customers arriving at random intervals
53+
time.sleep(random.randint(1, 3))
54+
executor.submit(add_customer_to_queue, name)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import threading
2+
import time
3+
from concurrent.futures import ThreadPoolExecutor
4+
5+
6+
class BankAccount:
7+
def __init__(self):
8+
self.balance = 0
9+
self.lock = threading.Lock()
10+
11+
def deposit(self, amount):
12+
print(
13+
f"Thread {threading.current_thread().name} waiting "
14+
"to acquire lock for deposit()"
15+
)
16+
with self.lock:
17+
print(
18+
f"Thread {threading.current_thread().name} "
19+
"acquired lock for deposit()"
20+
)
21+
time.sleep(0.1)
22+
self._update_balance(amount)
23+
24+
def _update_balance(self, amount):
25+
print(
26+
f"Thread {threading.current_thread().name} waiting to acquire "
27+
"lock for _update_balance()"
28+
)
29+
with self.lock: # This will cause a deadlock
30+
print(
31+
f"Thread {threading.current_thread().name} "
32+
"acquired lock for _update_balance()"
33+
)
34+
self.balance += amount
35+
36+
37+
account = BankAccount()
38+
39+
with ThreadPoolExecutor(
40+
max_workers=3, thread_name_prefix="Worker"
41+
) as executor:
42+
for _ in range(3):
43+
executor.submit(account.deposit, 100)
44+
45+
46+
print(f"Final balance: {account.balance}")

thread-safety-locks/bank_event.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import threading
2+
import time
3+
from concurrent.futures import ThreadPoolExecutor
4+
5+
bank_open = threading.Event()
6+
transactions_open = threading.Event()
7+
8+
9+
def serve_customer(customer_data):
10+
print(f"{customer_data['name']} is waiting for the bank to open.")
11+
12+
bank_open.wait()
13+
print(f"{customer_data['name']} entered the bank")
14+
if customer_data["type"] == "WITHDRAW_MONEY":
15+
print(f"{customer_data['name']} is waiting for transactions to open.")
16+
transactions_open.wait()
17+
print(f"{customer_data['name']} is starting their transaction.")
18+
19+
# Simulate the time taken for performing the transaction
20+
time.sleep(2)
21+
22+
print(f"{customer_data['name']} completed transaction and exited bank")
23+
else:
24+
# Simulate the time taken for banking
25+
time.sleep(2)
26+
print(f"{customer_data['name']} has exited bank")
27+
28+
29+
customers = [
30+
{"name": "Customer 1", "type": "WITHDRAW_MONEY"},
31+
{"name": "Customer 2", "type": "CHECK_BALANCE"},
32+
{"name": "Customer 3", "type": "WITHDRAW_MONEY"},
33+
{"name": "Customer 4", "type": "WITHDRAW_MONEY"},
34+
]
35+
36+
with ThreadPoolExecutor(max_workers=4) as executor:
37+
for customer_data in customers:
38+
executor.submit(serve_customer, customer_data)
39+
40+
print("Bank manager is preparing to open the bank.")
41+
time.sleep(2)
42+
print("Bank is now open!")
43+
bank_open.set() # Signal that the bank is open
44+
45+
time.sleep(3)
46+
print("Transactions are now open!")
47+
transactions_open.set()
48+
49+
50+
print("All customers have completed their transactions.")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import time
2+
from concurrent.futures import ThreadPoolExecutor
3+
4+
5+
class BankAccount:
6+
def __init__(self):
7+
self.balance = 1000
8+
9+
def withdraw(self, amount):
10+
if self.balance >= amount:
11+
new_balance = self.balance - amount
12+
time.sleep(0.1) # Simulate a delay
13+
self.balance = new_balance
14+
else:
15+
raise ValueError("Insufficient balance")
16+
17+
18+
account = BankAccount()
19+
20+
with ThreadPoolExecutor(max_workers=2) as executor:
21+
executor.submit(account.withdraw, 500)
22+
executor.submit(account.withdraw, 700)
23+
24+
print(f"Final account balance: {account.balance}")

thread-safety-locks/bank_rlock.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import threading
2+
import time
3+
from concurrent.futures import ThreadPoolExecutor
4+
5+
6+
class BankAccount:
7+
def __init__(self):
8+
self.balance = 0
9+
self.lock = threading.RLock()
10+
11+
def deposit(self, amount):
12+
print(
13+
f"Thread {threading.current_thread().name} "
14+
"waiting to acquire lock for .deposit()"
15+
)
16+
with self.lock:
17+
print(
18+
f"Thread {threading.current_thread().name} "
19+
"acquired lock for .deposit()"
20+
)
21+
time.sleep(0.1)
22+
self._update_balance(amount)
23+
24+
def _update_balance(self, amount):
25+
print(
26+
f"Thread {threading.current_thread().name} "
27+
"waiting to acquire lock for ._update_balance()"
28+
)
29+
with self.lock:
30+
print(
31+
f"Thread {threading.current_thread().name} "
32+
"acquired lock for ._update_balance()"
33+
)
34+
self.balance += amount
35+
36+
37+
account = BankAccount()
38+
39+
with ThreadPoolExecutor(
40+
max_workers=3, thread_name_prefix="Worker"
41+
) as executor:
42+
for _ in range(3):
43+
executor.submit(account.deposit, 100)
44+
45+
46+
print(f"Final balance: {account.balance}")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import random
2+
import threading
3+
import time
4+
from concurrent.futures import ThreadPoolExecutor
5+
6+
# Semaphore with a maximum of 2 resources (tellers)
7+
teller_semaphore = threading.Semaphore(2)
8+
9+
10+
def now():
11+
return time.strftime("%H:%M:%S")
12+
13+
14+
def serve_customer(name):
15+
print(f"{now()}: {name} is waiting for a teller.")
16+
with teller_semaphore:
17+
print(f"{now()}: {name} is being served by a teller.")
18+
# Simulate the time taken for the teller to serve the customer
19+
time.sleep(random.randint(1, 3))
20+
print(f"{now()}: {name} is done being served.")
21+
22+
23+
customers = [
24+
"Customer 1",
25+
"Customer 2",
26+
"Customer 3",
27+
"Customer 4",
28+
"Customer 5",
29+
]
30+
31+
with ThreadPoolExecutor(max_workers=5) as executor:
32+
for customer_name in customers:
33+
thread = executor.submit(serve_customer, customer_name)
34+
35+
36+
print(f"{now()}: All customers have been served.")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import threading
2+
import time
3+
from concurrent.futures import ThreadPoolExecutor
4+
5+
6+
class BankAccount:
7+
def __init__(self, balance=0):
8+
self.balance = balance
9+
self.account_lock = threading.Lock()
10+
11+
def withdraw(self, amount):
12+
with self.account_lock:
13+
if self.balance >= amount:
14+
new_balance = self.balance - amount
15+
print(f"Withdrawing {amount}...")
16+
time.sleep(0.1) # Simulate a delay
17+
self.balance = new_balance
18+
else:
19+
raise ValueError("Insufficient balance")
20+
21+
def deposit(self, amount):
22+
with self.account_lock:
23+
new_balance = self.balance + amount
24+
print(f"Depositing {amount}...")
25+
time.sleep(0.1) # Simulate a delay
26+
self.balance = new_balance
27+
28+
29+
account = BankAccount(1000)
30+
31+
with ThreadPoolExecutor(max_workers=3) as executor:
32+
executor.submit(account.withdraw, 700)
33+
executor.submit(account.deposit, 1000)
34+
executor.submit(account.withdraw, 300)
35+
36+
37+
print(f"Final account balance: {account.balance}")

0 commit comments

Comments
 (0)