Last Updated on September 12, 2022
You can get results from tasks in the ThreadPoolExecutor by calling the result() function.
In this tutorial, you will discover how to get results from tasks submitted to the ThreadPoolExecutor in Python.
Let’s get started.
Get Results from the ThreadPoolExecutor
The ThreadPoolExecutor in Python provides a pool of reusable threads for executing ad hoc tasks.
You can submit tasks to the thread pool by calling the submit() function and passing in the name of the function you wish to execute on another thread.
Calling the submit() function will return a Future object that allows you to check on the status of the task and get the result from the task once it completes.
You can also submit tasks by calling the map() function and specifying the name of the function to execute and the iterable of items to which your function will be applied.
Tasks submitted to the thread pool are defined by a custom function that may return a value. The returned value is likely the result of the completed task.
You need a way to get the results from completed tasks.
How do you get results from tasks submitted to the ThreadPoolExecutor?
Run loops using all CPUs, download your FREE book to learn how.
Call result() to Get the Results of Tasks
You can get the result returned by a target task function by calling the result() function on the Future object associated with the task.
Recall that you can submit tasks to the thread pool by calling submit(), which will return a Future object.
If your target task function returns a value once the task is complete, you can retrieve it directly by calling result().
1 2 3 |
... # get the result of a task result = future.result() |
The result() function is a blocking call.
This means that if the task is not yet done because it is scheduled (queued) or is running, then the result() task will not return until the task is complete.
You can set a time limit in seconds for how long you are willing to wait for a result by setting the timeout argument. By default, there is no timeout when waiting for a result.
1 2 3 |
... # get the result of a task with a timeout result = future.result(timeout=5) |
If the timeout is exceeded before a result is available, then a TimeoutError will be raised by the call to result(), which you may have to handle.
If the target task function raises an exception during execution, then the exception will be re-thrown by the thread pool when calling the result() function, which may need to be handled.
1 2 3 4 5 6 |
... # get the result and handle a known possible exception try: result = future.result() exception: # handle exception... |
Alternatively, you may have submitted tasks to the thread pool via the map() function.
Recall that this is a asynchronous version of the built-in map() function and will apply a specified function to each item in a provided iterable.
The map() function in ThreadPoolExecutor will return an iterable for results that you can enumerate directly to get the return value from each task. The iterator will progress as results become available in the order that tasks were submitted to the thread pool.
1 2 3 4 |
... # submit tasks and get results for result in executor.map(work, range(3)): # process result... |
Now that we know how to get results from tasks submitted to the ThreadPoolExecutor, let’s look at a worked example.
Example of Getting a Result From a Task
Let’s take a look at a worked example of getting results from the ThreadPoolExecutor.
First, let’s define a simple task that will sleep for a moment and return a string.
1 2 3 4 |
# mock task that will sleep for a moment def work(): sleep(1) return 'Task is complete' |
Next, we can start a thread pool using the context manager and submit a task by calling submit() that will return a Future object immediately.
1 2 3 4 5 |
... # create a thread pool with ThreadPoolExecutor() as executor: # execute the task future = executor.submit(work) |
We can then call the result() function on the Future object that will block until the task is completed and return the result from the task.
1 2 3 |
... # get the result from the task result = future.result() |
We can then handle the result; in this case, we simply print it.
1 2 3 |
... # report the result print(result) |
Tying this together, the complete example of getting a result from a task in the ThreadPoolExecutor is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# SuperFastPython.com # example of getting results from a task in the thread pool from time import sleep from concurrent.futures import ThreadPoolExecutor # mock task that will sleep for a moment def work(): sleep(1) return 'Task is complete' # create a thread pool with ThreadPoolExecutor() as executor: # execute the task future = executor.submit(work) # get the result from the task result = future.result() # report the result print(result) |
Running the example creates the thread pool and submits the task.
We then attempt to get the result and wait for the task to complete before the result is available.
Once the result is available, we report it to the user by calling print.
1 |
Task is complete |
Free Python ThreadPoolExecutor Course
Download your FREE ThreadPoolExecutor PDF cheat sheet and get BONUS access to my free 7-day crash course on the ThreadPoolExecutor API.
Discover how to use the ThreadPoolExecutor class including how to configure the number of workers and how to execute tasks asynchronously.
Example of Getting a Result From a Task With a Timeout
We can also set a time limit on how long we are willing to wait for a result.
This can be achieved by setting the timeout argument to a value in seconds when calling the result() function.
If the specified timeout elapses before the task is done and result is available, then a TimeoutError is raised, which may need to be handled.
1 2 3 4 5 6 7 8 |
... try: # get the result from the task result = future.result(timeout=0.5) # report the result print(result) except TimeoutError: print('Waited too long for a result') |
Tying this together, the example below demonstrates how to get a result from a task in the ThreadPoolExecutor, but only waiting a fixed time for the result.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# SuperFastPython.com # example of getting results from a task in the thread pool from time import sleep from concurrent.futures import ThreadPoolExecutor from concurrent.futures import TimeoutError # mock task that will sleep for a moment def work(): sleep(1) return 'Task is complete' # create a thread pool with ThreadPoolExecutor() as executor: # execute the task future = executor.submit(work) try: # get the result from the task result = future.result(timeout=0.5) # report the result print(result) except TimeoutError: print('Waited too long for a result') |
Running the example waits a moment for a result before giving up, as we expected.
The TimeoutError is handled and a message is reported that we were unable to get the result in time.
1 |
Waited too long for a result |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Getting Results From Tasks When Calling map()
You can also get the result for tasks submitted to the thread pool by calling map().
The results are provided in an iterable returned by calling map() that will return the results from tasks as they become available in the order that tasks were submitted to the thread pool.
We can update our task to take an argument and to include the argument in the result returned by the function.
1 2 3 4 |
# mock task that will sleep for a moment def work(value): sleep(1) return f'Task is done: {value}' |
We can then submit tasks to the thread pool by calling map() with an iterable of arguments, one for each task to submit.
The map() function returns an iterable of results immediately and will yield results as they become available for the tasks in order.
1 2 3 4 |
... # submit tasks and process results for result in executor.map(work, range(3)): print(result) |
Tying this together, the complete example of getting results from tasks in the ThreadPoolExecutor submitted by calling map() is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# SuperFastPython.com # example of getting results when calling map from time import sleep from concurrent.futures import ThreadPoolExecutor # mock task that will sleep for a moment def work(value): sleep(1) return f'Task is done: {value}' # create a thread pool with ThreadPoolExecutor() as executor: # submit tasks and process results for result in executor.map(work, range(3)): print(result) |
Running the example, the thread pool is created as per normal.
Three tasks are then submitted into the thread pool and we iterate over their results in order as they become available.
1 2 3 |
Task is done: 0 Task is done: 1 Task is done: 2 |
Further Reading
This section provides additional resources that you may find helpful.
Books
- ThreadPoolExecutor Jump-Start, Jason Brownlee, (my book!)
- Concurrent Futures API Interview Questions
- ThreadPoolExecutor Class API Cheat Sheet
I also recommend specific chapters from the following books:
- Effective Python, Brett Slatkin, 2019.
- See Chapter 7: Concurrency and Parallelism
- Python in a Nutshell, Alex Martelli, et al., 2017.
- See: Chapter: 14: Threads and Processes
Guides
- Python ThreadPoolExecutor: The Complete Guide
- Python ProcessPoolExecutor: The Complete Guide
- Python Threading: The Complete Guide
- Python ThreadPool: The Complete Guide
APIs
References
Takeaways
You now know how to get results from tasks submitted to the ThreadPoolExecutor.
Do you have any questions about how to get results?
Ask your question in the comments below and I will do my best to answer.
Photo by Carl Nenzen Loven on Unsplash
Do you have any questions?