Explain Codes LogoExplain Codes Logo

How to get the return value from a thread?

python
threading
concurrent-futures
asyncio
Nikita BarsukovbyNikita Barsukov·Dec 24, 2024
TLDR

Need a thread's return value sans the complicated dance? Use concurrent.futures.ThreadPoolExecutor:

from concurrent.futures import ThreadPoolExecutor def task_to_run(arg): # Replace below with your function's logic # Add wheels to the inanimate carbon rod return arg + 10 with ThreadPoolExecutor() as executor: # Schedule function and feed it the rod future = executor.submit(task_to_run, 5) # Await magic and proclaim the result! result = future.result() print(f'Result: {result}')

executor.submit() kickstarts the function. future.result() waits patiently for completion, holding the results once done.

Thread-return techniques

For the thread warriors, a cache of tools to retrieve thread return values:

Thread-safe Queues

Queues can act as a communication bridge between threads. Have the worker store the return value in the queue, then just sit back and pick up the results.

from threading import Thread from queue import Queue def worker(queue, arg): # Crew member stash the secret formula queue.put(arg + 10) # The secretive queue queue = Queue() thread = Thread(target=worker, args=(queue, 5)) thread.start() thread.join() # Voila! The secret formula result = queue.get() print(f'Result: {result}')

Custom join with Thread subclasses

How about the ninja technique? Subclass Thread, customize the join() method, and boom—you've got the thread return value.

from threading import Thread class ReturnThread(Thread): def run(self): # Stealthily pass secret to _return self._return = self._target(*self._args, **self._kwargs) def join(self, *args, **kwargs): super().join(*args, **kwargs) # Like a ninja, _return vanishes post-mission return self._return # Your undercover ninja agent thread = ReturnThread(target=task_to_run, args=(5,)) thread.start() # Ninja report! result = thread.join() print(f'Result: {result}')

Async to the rescue

The multiprocessing.pool.ThreadPool method comes with apply_async for a non-blocking execution. Fetch the values later, just like getting your online shopping delivery!

from multiprocessing.pool import ThreadPool # Your shopping agent pool = ThreadPool(processes=1) # Placing order for secret formula async_result = pool.apply_async(task_to_run, (5,)) # Finally, the delivery! result = async_result.get() print(f'Result: {result}')

Asyncio kingdom

Working with asyncio? Get your concurrent.futures with a little dressing of asyncio.Future. Now they are on a par with asyncio coroutines.

import asyncio from concurrent.futures import ThreadPoolExecutor async def run_in_executor(executor, fn, *args): loop = asyncio.get_running_loop() future = loop.run_in_executor(executor, fn, *args) # A waited future tastes better return await future async def main(): with ThreadPoolExecutor() as executor: result = await run_in_executor(executor, task_to_run, 5) print(f'Result: {result}') # Let the async harmony play asyncio.run(main())

Using modern Python for thread returns

Wield the tools of Python 3 to your benefit. They provide a more efficient and intuitive means to handle thread results.

Rejoice with concurrent.futures (Python 3.2+)

No need for handling each thread with care. With ThreadPoolExecutor from concurrent.futures, wrangling thread results is just a walk in the park.

# You've seen this magic in the Fast answer section!

Lambda + Queue: The power duo

In complex scenarios, lambda functions combined with Queues carry worker's return value safely to the main thread.

queue = Queue() # Lambda in shining armour thread = Thread(target=lambda q, arg1: q.put(task_to_run(arg1)), args=(queue, 5)) thread.start() thread.join() # The treasure! result = queue.get() print(f'Result: {result}')

Asyncio candy wrap of future

Wrap futures from ThreadPoolExecutor with asyncio.wrap_future to make future objects asyncio-friendly.

# Find a part of this saga in the Asyncio kingdom section!

Stellar management of threads and their return values paves the way to a robust application that's not just performant, but also reliable and maintainable.