Using one thread per item

Now, it is time for improvement. We don't do a lot of processing in Python, and long execution times are caused by communication with the external service. We send an HTTP request to the remote server, it calculates the answer, and then we wait until the response is transferred back. There is a lot of I/O involved, so multithreading seems like a viable option. We can start all the requests at once in separate threads and then just wait until we receive data from all of them. If the service that we are communicating with is able to process our requests concurrently, we should definitely see a performance improvement.

So, let's start with the easiest approach. Python provides clean and easy to use abstraction over system threads with the threading module. The core of this standard library is theĀ Thread class, which represents a single thread instance. Here is a modified version of the main() function that creates and starts a new thread for every place to geocode and then waits until all the threads finish:

from threading import Thread

def main():
threads = []
for base in BASES:
thread = Thread(target=fetch_rates, args=[base])
thread.start()
threads.append(thread)

while threads:
threads.pop().join()

It is a quick and dirty solution that approaches the problem in a bit of a frivolous way. And it is not a way to write reliable software that will serve thousands or millions of users. It has some serious issues that we will have to address later. But hey, it works, as we can see from the following code:

$ python3 threads_one_per_item.py
1 CZK = 0.044 USD, 0.039 EUR, 0.167 PLN, 0.375 NOK, 1.0 CZK
1 NOK = 0.117 USD, 0.104 EUR, 0.446 PLN, 1.0 NOK, 2.66 CZK
1 USD = 1.0 USD, 0.887 EUR, 3.8 PLN, 8.53 NOK, 22.7 CZK
1 EUR = 1.13 USD, 1.0 EUR, 4.29 PLN, 9.62 NOK, 25.6 CZK
1 PLN = 0.263 USD, 0.233 EUR, 1.0 PLN, 2.24 NOK, 5.98 CZK
time elapsed: 0.13s

And it is also considerably faster.

So, when we know that threads have a beneficial effect on our application, it is time to use them in a saner way. First, we need to identify the following issues in the preceding code:

In the next section, we will see how to use a thread pool.