# CSIE 4004 HW4 report
### Problem description
design a 3 threads python program, first thread is responsible for accepting clients' requests and return a number `NUM` based on number `A` and number `B`, and second thread is responsible for updating number `A` by calling a API regularly. third thread is responsible for updating number `B` by calling another API regularly. you can init the update an arbitrary interval t for second and third thread.
### first prompt
> Enter the description above.
```python=
import threading
import time
import random
# Global variables
NUM = 0
A = 0
B = 0
# Function to handle client requests
def handle_requests():
global NUM, A, B
while True:
# Simulating client request processing
# In this example, NUM is the sum of A and B
NUM = A + B
print(f"Client Request: A={A}, B={B}, NUM={NUM}")
time.sleep(1)
# Function to update number A
def update_number_A():
global A
while True:
# Simulating API call to update A
# In this example, A is a random number
A = random.randint(1, 10)
print(f"Updated A: {A}")
time.sleep(5)
# Function to update number B
def update_number_B():
global B
while True:
# Simulating API call to update B
# In this example, B is fetched from an API
B = random.randint(1, 10)
print(f"Updated B: {B}")
time.sleep(7)
# Creating threads
requests_thread = threading.Thread(target=handle_requests)
update_A_thread = threading.Thread(target=update_number_A)
update_B_thread = threading.Thread(target=update_number_B)
# Starting threads
requests_thread.start()
update_A_thread.start()
update_B_thread.start()
```
### second prompt
> the first thread is responsible for handling incoming requests, you should init a server no matter base on TCP or UDP.
```python=
def handle_client(conn):
global NUM, A, B
while True:
# Simulating client request processing
# In this example, NUM is the sum of A and B
result = A + B
NUM = result
response = f"A={A}, B={B}, NUM={NUM}"
conn.send(response.encode())
time.sleep(1)
# Function to handle incoming client connections
def accept_clients():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", 8888))
server_socket.listen(5)
print("Server started. Listening for incoming connections...")
while True:
conn, addr = server_socket.accept()
print(f"Accepted connection from {addr}")
client_thread = threading.Thread(target=handle_client, args=(conn,))
client_thread.start()
```
### Third prompt
> the function handle_client won't leave although it has send response back, you should close the connection automatically.
```python=
def handle_client(conn):
global NUM, A, B
# Simulating client request processing
# In this example, NUM is the sum of A and B
NUM = A + B
response = f"A={A}, B={B}, NUM={NUM}"
conn.send(response.encode())
time.sleep(1)
conn.close()
```
### Fourth prompt
> Although python has GIL, it might still have concurrent read and write to number A and B due to multi-threading. can you avoid such situation with lock?
```diff
+ a_lock = threading.Lock()
+ b_lock = threading.Lock()
def handle_client(conn):
global NUM, A, B
# Acquire locks before accessing A and B
+ with a_lock:
a_value = A
+ with b_lock:
b_value = B
# Calculate NUM based on A and B
NUM = a_value + b_value
response = f"A={a_value}, B={b_value}, NUM={NUM}"
conn.send(response.encode())
time.sleep(1)
conn.close()
def update_number_A():
global A
while True:
# Simulating API call to update A
# In this example, A is a random number
new_a = random.randint(1, 10)
+ with a_lock:
A = new_a
print(f"Updated A: {A}")
time.sleep(5)
# Function to update number B
def update_number_B():
global B
while True:
# Simulating API call to update B
# In this example, B is fetched from an API
response = requests.get("https://api.example.com/get_b")
new_b = int(response.json()["value"])
+ with b_lock:
B = new_b
print(f"Updated B: {B}")
time.sleep(7)
```
### Execute result
1. Copy the below final code as `test.py`
2. execute it with `python3 test.py` in terminal.
3. Open another terminal and execute `telnet localhost 8888`
4. See the following result.

### Final Code
```python=
import threading
import time
import random
import socket
# Global variables
NUM = 0
A = 0
B = 0
# Locks for synchronization
a_lock = threading.Lock()
b_lock = threading.Lock()
# Function to handle client requests
def handle_client(conn):
global NUM, A, B
# Acquire locks before accessing A and B
with a_lock:
a_value = A
with b_lock:
b_value = B
# Calculate NUM based on A and B
NUM = a_value + b_value
response = f"A={a_value}, B={b_value}, NUM={NUM}"
conn.send(response.encode())
time.sleep(1)
conn.close()
# Function to update number A
def update_number_A():
global A
while True:
# Simulating API call to update A
# In this example, A is a random number
new_a = random.randint(1, 10)
with a_lock:
A = new_a
print(f"Updated A: {A}")
time.sleep(5)
# Function to update number B
def update_number_B():
global B
while True:
# Simulating API call to update B
# In this example, B is fetched from an API
new_b = random.randint(1, 10)
with b_lock:
B = new_b
print(f"Updated B: {B}")
time.sleep(7)
# Function to handle incoming client connections
def accept_clients():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("127.0.0.1", 8888))
server_socket.listen(5)
print("Server started. Listening for incoming connections...")
while True:
conn, addr = server_socket.accept()
print(f"Accepted connection from {addr}")
client_thread = threading.Thread(target=handle_client, args=(conn,))
client_thread.start()
# Creating threads
client_requests_thread = threading.Thread(target=accept_clients)
update_A_thread = threading.Thread(target=update_number_A)
update_B_thread = threading.Thread(target=update_number_B)
# Starting threads
client_requests_thread.start()
update_A_thread.start()
update_B_thread.start()
```