How to Return From a Thread

Python allows us to spawn new threads of execution through an elegant API by using the threading standard module. Here is a simple example program that runs a worker() function within a new thread:

import threading
import time
def worker():
    """
    This functions runs in a new thread.
    """
    # Run some random code.
    for _ in range(3):
        print("Hello from thread!")
        time.sleep(1)
# Set up the thread instance and start execution.
t = threading.Thread(target=worker)
t.start()

This kind of procedure is useful when there is need to execute a heavy task on the background without blocking the main thread, specially when the main thread runs our app's UI. But what happens if worker() returns a value that we actually need in the main thread, or wherever t.start() was called?

Unfortunately, you cannot return a value from a function that runs in thread B and read it from thread A. A tempting approach, recommended elsewhere on the Internet, is to use a global variable or class attribute shared across threads. Do not do this, please. Most Python objects cannot be safely shared between threads without some sync mechanism, besides the fact that the usage of globals leads to poor-quality code. There is no need to do so, anyway. A better, safe and non-buggy solution is to use the standard queue.Queue datatype, which is thread-safe. Just create the queue in the main thread, pass it as an argument to the new thread, put the result of the function in the queue, and retrieve it back in the main thread. Here is an example:

import threading
import time
import queue
def worker(q: queue.Queue):
    """
    This functions runs in a new thread.
    """
    # Run some random code.
    for _ in range(3):
        print("Hello from thread!")
        time.sleep(1)
    # Put a random return value (5) in the queue.
    q.put_nowait(5)
# Thread-safe queue where the return value will be stored.
q = queue.Queue()
# Set up the thread instance and start execution.
t = threading.Thread(target=worker, args=(q,))
t.start()
# Wait until the thread finishes, otherwise the
# return value won't be ready.
t.join()
# Retrieve the result.
result = q.get_nowait()
print("Thread result:", result)

Comments