Seminar 2: Remote Procedure Calls (RPC) with threads in python
Goal: To use Remote Procedure Calls (RPC) together with (multi) threads in python.
Definitions: Threads are the unit of execution on a processor. When a program is run on your computer, it then starts up a process. That process can then be made up of one or more threads which execute different (computing) tasks.
- All the threads in one process share memory.
- All the threads have access to global variables.
- Each thread has kept its own stack, program counter and registers.
The importance of threads:
- Concurrency. Concurrency allows us to schedule multiple tasks on a single processor. These tasks are running simultaneously (with non-deterministic interleaving) and essentially they share CPU time to perform some computation.
- For example: with I/O concurrency, instead of waiting for an I/O operation to complete before continuing execution (thereby rendering the CPU idle), threads allow us to perform other tasks while we wait.
- Parallelism: We can perform multiple tasks in parallel on several cores. Parallelism allows multiple tasks to perform computations at the same time since they are executing on different CPU cores. Typically, parallelism aims to split the complexity of a task into smaller computing problems and it is constrained to an execution time (deadline)
- Convenience: Threads provide a convenient way to execute short-lived tasks in the background e.g. a master node continuously polling a worker to check if it's alive.
Threading Challenges:
- Deadlock: This happens when two or more threads are waiting on each other in such a way that neither can progress.
- Race: when accessing shared data: What happens if two threads do n = n + 2 at the same time? Or one thread reads a value while another one increments it? An alternative to that is to avoid sharing mutable data.
- Coordination: If one thread is producing data while another is consuming that data, "How can the consumer wait for data to be produced, and release the CPU while waiting?" or "How can the producer then wake up the consumer?"
Prerequisites: Python (Python Download), "RPyC" (RPyC Installation).
- Python installation: https://realpython.com/installing-python/
Exercise: We will get acquainted with thread in RPC procedure by still using RPyC in Python. In this seminar, we will use the module ThreadedServer. ThreadedServer is a Custom RPyC Servers which is easy to start the thread in RPC. It is a forking server that forks a child-process to handle each incoming connection.
- The code for the above implementation is given here for you. (Code)
- Server: rpyc_server.py. Using ThreadedServer module
- Client: rpyc_client.py.
- We still use the same computer to run both client and server
In the ThreadedServer:
import rpyc from rpyc.utils.server import ThreadedServer # or ForkingServer class MyService(rpyc.Service): # # ... you service's implementation # if __name__ == "__main__": server = ThreadedServer(MyService, port = 12345) server.start()
WORKING PIPELINE OF THE ABOVE IMPLEMENTATION:
Running a Server
$ python rpyc_server.py
Running a Client
$ python rpyc_client.py localhost
In the rpyc_server.py
import rpyc from rpyc.utils.server import ThreadedServer import datetime date_time=datetime.datetime.now() class MonitorService(rpyc.Service): def on_connect(self,conn): print("\nconnected on {}".format(date_time)) def on_disconnect(self,conn): print("disconnected on {}\n".format(date_time)) if __name__==_main_: t=ThreadedServer(MonitorService, port=18812) # Threadedserver execute the service t.start()
In the rpyc_client.py
conn = rpyc.connect(server,18812)
Task: Define another function you like with arguments in the rpyc-server.py, which can print the result revelant to the argument when the client passes it to the server. The results are printed in the server side and the client just passes the arguments. TIPS (Other methods to achieve the funtion are also good):
- In the client part: you can use root as a reference. To the remote party, the service is exposed as the root object of the connection, e.g.,conn.root. It can be used access and invoke exposed attributes and methods.(root)
- In the server part: you should use prefix "exposed_" (the prefix of exposed attributes) to name your function.
Possible issues: if you meet some issues of running the code, you can change another port number to try it.
Deliverables: Zip file containing the source code file and the screenshots of the server and the client terminals.
Link:
NOTE: Watch the video if something is not clear.