Last Updated on September 12, 2022
You can return values from a thread via instance variables on the threading.Thread class or via global variables.
In this tutorial you will discover how to return values from a thread.
Let’s get started.
Need to Return Values From a Thread
A thread is a thread of execution in a computer program.
Every Python program has at least one thread of execution called the main thread. Both processes and threads are created and managed by the underlying operating system.
Sometimes we may need to create additional threads in our program in order to execute code concurrently.
Python provides the ability to create and manage new threads via the threading module and the threading.Thread class.
You can learn more about Python threads in the guude:
When using new threads, we may need to return a value from the thread to another thread, such as the main thread.
This may be for many reasons, such as:
- The new thread loaded some data that needs to be returned.
- The new thread calculated something that needs to be returned.
- The new thread needs to share its state or status with another thread.
How can we return values from a thread in Python?
Run loops using all CPUs, download your FREE book to learn how.
How to Return Values From a Thread
A thread cannot return values directly.
The start() method on a thread calls the run() method of the thread that executes our code in a new thread of execution. The run() method in turn may call a target function, if configured.
The start() method does not block, instead it returns immediately and does not return a value. The run method also does not return a value.
We can join a new thread from the current thread and block until the new thread terminates, but the join() method also does not return a value.
Instead, we must return values from a thread indirectly.
There are two main ways to return values from a thread, they are:
- Extend threading.Thread and store data in instance variables.
- Store data in global variables.
The preferred approach is to extend the threading.Thread class and store return data as instance variables.
This involves defining a new class that extends the threading.Thread and defines a constructor that calls the parent constructor.
The run() method can be defined that executes the custom code in a new thread and stores data as instance variables.
1 2 3 4 5 6 7 8 9 |
# custom thread class CustomThread(Thread): # constructor def __init__(self): # execute the base constructor Thread.__init__(self) # function executed in a new thread def run(self): # ... |
An alternate approach is to store data from the thread in a global variable.
All threads will be able to access global variables. This may be a preferred approach if you already have a function executing in a new thread and need to get data out of the thread.
The scope of the global variable can be made explicit and data can be stored directly.
1 2 3 |
... # correctly scope the global variable global data |
The danger of both approaches is that there could be a race condition between the new thread storing data in an instance variable or global variable and one or more other threads reading that data.
The solution could be to protect the data with a threading.Lock, to use a threading.Event to flag that the return data is available, or to use a threading.Condition to notify threads that the data is ready.
A simpler approach is to simply wait for the new thread to terminate.
Now that we know how to return values from a thread, let’s look at some worked examples.
Example of Returning a Value From a Thread
We can explore how to simulate returning a single value from a thread via an instance variable.
In this example we will define a new class that extends the threading.Thread class, define the instance variable in the constructor and set it to None, then override the run() function with custom code and set the instance variable. The main thread will then access the return value.
First, let’s define the new class that extends the Threading.Thread class and defines a constructor that calls the parent constructor.
In the constructor we will also define a new public property named “value” and set it to the value None.
1 2 3 4 5 6 7 8 |
# custom thread class CustomThread(Thread): # constructor def __init__(self): # execute the base constructor Thread.__init__(self) # set a default value self.value = None |
Next, we can override the run() method on threading.Thread to execute some custom code.
In this case, we will block for a moment, then store data on the “value” instance variable.
1 2 3 4 5 6 |
# function executed in a new thread def run(self): # block for a moment sleep(1) # store data in an instance variable self.value = 'Hello from a new thread' |
Tying this together, the complete new class is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# custom thread class CustomThread(Thread): # constructor def __init__(self): # execute the base constructor Thread.__init__(self) # set a default value self.value = None # function executed in a new thread def run(self): # block for a moment sleep(1) # store data in an instance variable self.value = 'Hello from a new thread' |
Next, the main thread can create an instance of our new thread and then call the start() function to start executing it.
1 2 3 4 5 |
... # create a new thread thread = CustomThread() # start the thread thread.start() |
The main thread will then join this new thread and block until it has terminated.
1 2 3 |
... # wait for the thread to finish thread.join() |
Once the new thread has terminated, the main thread knows that the variable was set.
We can access it directly and report the value.
1 2 3 4 |
... # get the value returned from the thread data = thread.value print(data) |
Tying this together, the complete example of returning a value from a thread is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# SuperFastPython.com # example of returning a value from a thread from time import sleep from threading import Thread # custom thread class CustomThread(Thread): # constructor def __init__(self): # execute the base constructor Thread.__init__(self) # set a default value self.value = None # function executed in a new thread def run(self): # block for a moment sleep(1) # store data in an instance variable self.value = 'Hello from a new thread' # create a new thread thread = CustomThread() # start the thread thread.start() # wait for the thread to finish thread.join() # get the value returned from the thread data = thread.value print(data) |
Running the example first creates the new thread instance and starts the execution.
The main thread blocks until the new thread has completed, then accesses the instance variable and reports its value.
This simulates a simple single return value from the new thread.
1 |
Hello from a new thread |
Next, let’s look at how we might return multiple values from a thread.
Free Python Threading Course
Download your FREE threading PDF cheat sheet and get BONUS access to my free 7-day crash course on the threading API.
Discover how to use the Python threading module including how to create and start new threads and how to use a mutex locks and semaphores
Example of Returning Multiple Values From a Thread
We can explore simulating the return of multiple values from the new thread.
Using the “instance variables” technique, this involves simply defining and using multiple instance variables on our custom threading.Thread class.
We can update the above example to simulate returning multiple values.
First, we will follow the good practice of defining our properties in the constructor of the class and setting default values. In this case it will have three return values: “value1“, “value2“, and “value3“.
1 2 3 4 5 6 7 8 |
# constructor def __init__(self): # execute the base constructor Thread.__init__(self) # set a default values self.value1 = None self.value2 = None self.value3 = None |
Next, we can update the overridden run() method to set values in each instance variable.
1 2 3 4 5 6 7 8 |
# function executed in a new thread def run(self): # block for a moment sleep(1) # store data in an instance variable self.value1 = 'Hello from a new thread' self.value2 = 99 self.value3 = False |
Finally, we can update the main thread to access the instance variables like return values from the new thread.
1 2 3 4 5 |
... # report all values returned from a thread print(thread.value1) print(thread.value2) print(thread.value3) |
Tying this together, the complete example of returning multiple values from a thread is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# SuperFastPython.com # example of returning multiple value from a thread from time import sleep from threading import Thread # custom thread class CustomThread(Thread): # constructor def __init__(self): # execute the base constructor Thread.__init__(self) # set a default values self.value1 = None self.value2 = None self.value3 = None # function executed in a new thread def run(self): # block for a moment sleep(1) # store data in an instance variable self.value1 = 'Hello from a new thread' self.value2 = 99 self.value3 = False # create a new thread thread = CustomThread() # start the thread thread.start() # wait for the thread to finish thread.join() # report all values returned from a thread print(thread.value1) print(thread.value2) print(thread.value3) |
Running the example creates and runs the new thread.
The main thread blocks until the new thread has terminated and we know that the instance variables have been assigned data.
Finally, the main thread accesses the instance variables and reports their values, simulating multiple return values from the new thread.
1 2 3 |
Hello from a new thread 99 False |
Next, let’s look at how we might return values from a thread via a global variable.
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Returning Values Via Global Variables
We can explore how we might use global variables to simulate returning values from a new thread.
This approach might be preferred if we already have a function that we are running in a new thread.
First, we can define a new target task function to run in a new thread. The function will block for a moment, then declare the scope of a global variable and assign it a value.
1 2 3 4 5 6 7 8 |
# function executed in a new thread def task(): # block for a moment sleep(1) # correctly scope the global variable global data # store data in the global variable data = 'Hello from a new thread' |
The main thread will define the global variable and set a default value.
1 2 3 |
... # define the global variable data = None |
We can then create a new threading.Thread instance and have it call our new task() function by specifying it in the “target” variable of the constructor.
1 2 3 |
... # create a new thread thread = Thread(target=task) |
The main thread can then start the new thread and join it, blocking until the new thread has terminated.
1 2 3 4 5 |
... # start the thread thread.start() # wait for the thread to finish thread.join() |
Finally, once we know the new thread has terminated and that the global variable has been assigned by the new thread, we can report its value.
1 2 3 |
... # report the global variable print(data) |
Tying this together, the complete example of returning values from another thread via global variables is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# SuperFastPython.com # example of returning a value from a thread from time import sleep from threading import Thread # function executed in a new thread def task(): # block for a moment sleep(1) # correctly scope the global variable global data # store data in the global variable data = 'Hello from a new thread' # define the global variable data = None # create a new thread thread = Thread(target=task) # start the thread thread.start() # wait for the thread to finish thread.join() # report the global variable print(data) |
Running the example first creates the new thread and starts it, executing our custom target function.
The new thread then stores data against the global variable, being sure to explicitly specify the scope to avoid confusion in reading the code and possible bugs in the future.
THe main thread blocks until the new thread terminates then reports the simulated return value stored in the global variable.
1 |
Hello from a new thread |
Further Reading
This section provides additional resources that you may find helpful.
Python Threading Books
- Python Threading Jump-Start, Jason Brownlee (my book!)
- Threading API Interview Questions
- Threading Module API Cheat Sheet
I also recommend specific chapters in the following books:
- Python Cookbook, David Beazley and Brian Jones, 2013.
- See: Chapter 12: Concurrency
- 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 Threading: The Complete Guide
- Python ThreadPoolExecutor: The Complete Guide
- Python ThreadPool: The Complete Guide
APIs
References
Takeaways
You now know how to return values from a thread in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Harley-Davidson on Unsplash
Do you have any questions?