Get return value from thread python

I stole kindall's answer and cleaned it up just a little bit.

The key part is adding *args and **kwargs to join[] in order to handle the timeout

class threadWithReturn[Thread]:
    def __init__[self, *args, **kwargs]:
        super[threadWithReturn, self].__init__[*args, **kwargs]
        
        self._return = None
    
    def run[self]:
        if self._Thread__target is not None:
            self._return = self._Thread__target[*self._Thread__args, **self._Thread__kwargs]
    
    def join[self, *args, **kwargs]:
        super[threadWithReturn, self].join[*args, **kwargs]
        
        return self._return

UPDATED ANSWER BELOW

This is my most popularly upvoted answer, so I decided to update with code that will run on both py2 and py3.

Additionally, I see many answers to this question that show a lack of comprehension regarding Thread.join[]. Some completely fail to handle the timeout arg. But there is also a corner-case that you should be aware of regarding instances when you have [1] a target function that can return None and [2] you also pass the timeout arg to join[]. Please see "TEST 4" to understand this corner case.

ThreadWithReturn class that works with py2 and py3:

import sys
from threading import Thread
from builtins import super    # //stackoverflow.com/a/30159479

_thread_target_key, _thread_args_key, _thread_kwargs_key = [
    ['_target', '_args', '_kwargs']
    if sys.version_info >= [3, 0] else
    ['_Thread__target', '_Thread__args', '_Thread__kwargs']
]

class ThreadWithReturn[Thread]:
    def __init__[self, *args, **kwargs]:
        super[].__init__[*args, **kwargs]
        self._return = None
    
    def run[self]:
        target = getattr[self, _thread_target_key]
        if target is not None:
            self._return = target[
                *getattr[self, _thread_args_key],
                **getattr[self, _thread_kwargs_key]
            ]
    
    def join[self, *args, **kwargs]:
        super[].join[*args, **kwargs]
        return self._return

Some sample tests are shown below:

import time, random

# TEST TARGET FUNCTION
def giveMe[arg, seconds=None]:
    if not seconds is None:
        time.sleep[seconds]
    return arg

# TEST 1
my_thread = ThreadWithReturn[target=giveMe, args=['stringy',]]
my_thread.start[]
returned = my_thread.join[]
# [returned == 'stringy']

# TEST 2
my_thread = ThreadWithReturn[target=giveMe, args=[None,]]
my_thread.start[]
returned = my_thread.join[]
# [returned is None]

# TEST 3
my_thread = ThreadWithReturn[target=giveMe, args=['stringy',], kwargs={'seconds': 5}]
my_thread.start[]
returned = my_thread.join[timeout=2]
# [returned is None] # because join[] timed out before giveMe[] finished

# TEST 4
my_thread = ThreadWithReturn[target=giveMe, args=[None,], kwargs={'seconds': 5}]
my_thread.start[]
returned = my_thread.join[timeout=random.randint[1, 10]]

Can you identify the corner-case that we may possibly encounter with TEST 4?

The problem is that we expect giveMe[] to return None [see TEST 2], but we also expect join[] to return None if it times out.

returned is None means either:

[1] that's what giveMe[] returned, or

[2] join[] timed out

This example is trivial since we know that giveMe[] will always return None. But in real-world instance [where the target may legitimately return None or something else] we'd want to explicitly check for what happened.

Below is how to address this corner-case:

# TEST 4
my_thread = ThreadWithReturn[target=giveMe, args=[None,], kwargs={'seconds': 5}]
my_thread.start[]
returned = my_thread.join[timeout=random.randint[1, 10]]

if my_thread.isAlive[]:
    # returned is None because join[] timed out
    # this also means that giveMe[] is still running in the background
    pass
    # handle this based on your app's logic
else:
    # join[] is finished, and so is giveMe[]
    # BUT we could also be in a race condition, so we need to update returned, just in case
    returned = my_thread.join[]

Last Updated on September 9, 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.

Learn threading systematically with this 7-day jump-start.

Let’s get started.

Table of Contents

  • Need to Return Values From a Thread
  • Get your free mind map
  • How to Return Values From a Thread
  • Example of Returning a Value From a Thread
  • Example of Returning Multiple Values From a Thread
  • Example of Returning Values Via Global Variables
  • Further Reading
  • Takeaways

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:

  • Threading in Python: The Complete Guide

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?

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.

# custom thread

classCustomThread[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.

...

# correctly scope the global variable

globaldata

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.

How well do you know threading?
Find out with 120+ answers to interview questions. Learn more.

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.

# custom thread

classCustomThread[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.

# 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.

# custom thread

classCustomThread[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.

...

# 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.

...

# 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.

...

# 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

classCustomThread[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.

Next, let’s look at how we might return multiple values from a thread.

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“.

# 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.

# 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.

...

# 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

classCustomThread[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.

Hello from a new thread

99

False

Next, let’s look at how we might return values from a thread via a global variable.

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.

# 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.

...

# 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.

...

# 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.

...

# 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.

...

# 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

    globaldata

    # 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.

Further Reading

This section provides additional resources that you may find helpful.

  • threading - Thread-based parallelism
  • Threading: The Complete Guide
  • Threading Module API Cheat Sheet
  • Threading API Interview Questions
  • Threading Jump-Start [my 7-day course]

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

Can a thread return a value Python?

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.

How do I get the return value of a threaded function?

pool import ThreadPool pool = ThreadPool[processes=1] async_result = pool. apply_async[foo, ['world', 'foo']] # tuple of args for foo # do some other stuff in the main process return_val = async_result. get[] # get the return value from your function.

How do you return a value from thread run method in Java?

The thread that needs to return the value uses the Callable interface to implement the call method; Before getting the future object, you can use the isDone[] method to check whether the future is complete. After that, you can call the get[] method to get the value of the future.

Which is better multiprocessing or multithreading in Python?

Multiprocessing is a easier to just drop in than threading but has a higher memory overhead. If your code is CPU bound, multiprocessing is most likely going to be the better choice—especially if the target machine has multiple cores or CPUs.

Chủ Đề