Python Concurrency (iv)

Image for post
Image for post
Photo by Marco Savastano on Unsplash

Following up to last blog, we will be looking at the ThreadPoolExecutor, introduced in version 3.2 and provides a simple high-level interface for asynchronously executing input/output bound tasks.

Think of it as an abstraction around both Thread and Multiprocessing.

Image for post
Image for post
Same Process for Thread/Multiprocessing

The asynchronous execution can be performed with threads, using ThreadPoolExecutor, or separate processes, using ProcessPoolExecutor. Both implement the same interface, which is defined by the abstract Executor class.

Typically, for I/O bound task, it is recommended to execute as thread; for cpu bound tasks, it is recommended to execute as process.

Below are some of the methods built-in for the Executor class:

  • submit: schedules the callable, fn, to be executed as fn(*args **kwargs) and returns a Future object (we will talk about this later :)) representing the execution of the callable.
  • map: equivalent to map(func, *iterables) except func is executed asynchronously and apply items to the worker in the pool concurrently.
  • shutdown: signal the executor that it should free any resources that it is using when the currently pending futures are done executing.
Image for post
Image for post
Executor using submit
Image for post
Image for post
Executor using map

Note that you can use Context Manager with Executor so you don’t need to cal shutdown:

with executor:
...use executor

Below illustrates how to use with block with ThreadPoolExecutor.

Image for post
Image for post
example using ThreadPoolExecutor
  • First, we initiate the threadPoolExecutor in with clause so we don’t need to call shutdown on it;
  • Then we call the submit as it is non-blocking so will not return false even url2 does not exist;
  • The final try catch block wraps around .result() as it is only here where we need to handle the exceptions.

Now let’s move on to Future. The Concurrent.Futures module provides a high-level interface for asynchronously executing callable. The module is designed to be compatible with the Asyncio event loop (we will talk about it in next blog), so that it is easier to work with threads/processes while applying Asyncio driven application. It serves as a wrapper around low-level threading or multiprocessing modules so we can use a clean and modern Python API straight away.

A Future is an object that acts as a proxy for a result that is yet to be computed. Sounds confusing right? But if you know JavaScript, you can think of it as Promise.

The Future instances are created by Executor.submit():

future = executor.submit(func, args*)
... do other things...
result = future.result()

Below are Future methods:

  • cancel(): Attempt to cancel the call. If the call is currently being executed or finished running and cannot be cancelled then the method will return False, otherwise the call will be cancelled and the method will return True.
  • cancelled(): Return True if the call was successfully cancelled.
  • running(): Return True if the call is currently being executed and cannot be cancelled.
  • done(): Return True if the call was successfully cancelled or finished running.
  • result(): Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds.
  • add_done_callback(fn): Attaches the callable function to the future, this will be called when the future is cancelled or finishes running. If the future has already completed or been cancelled, fn will be called immediately.

That’s so much of it today!

In next blog we will look at Asyncio module (finally) — stay tuned :) .

Happy Reading!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store