# 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. ![](https://i.imgur.com/wmm6sbY.png) ### 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() ```