Python thread return result

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 function 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:

Читайте также:  Транспонирование прямоугольной матрицы java
import threading
import time
import queue
def worker ( q : queue . Queue ):
«»»
This function 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 )

Источник

How to Return Values from a Thread

Summary: in this tutorial, you will learn how to return values from a child thread to the main thread by extending the threading.Thread class.

When a Python program starts, it has a single thread called the main thread. Sometimes, you want to offload I/O tasks to new threads and execute them concurrently. Additionally, you may also want to get the return values from these threads from the main thread.

To return a value from a thread, you can extend the Thread class and store that value in the instance of the class.

The following example illustrates how to check a specified URL and return its HTTP status code in a separate thread:

from threading import Thread import urllib.request class HttpRequestThread(Thread): def __init__(self, url: str) -> None: super().__init__() self.url = url self.http_status_code = None self.reason = None def run(self) -> None: try: response = urllib.request.urlopen(self.url) self.http_status_code = response.code except urllib.error.HTTPError as e: self.http_status_code = e.code except urllib.error.URLError as e: self.reason = e.reason def main() -> None: urls = [ 'https://httpstat.us/200', 'https://httpstat.us/400' ] # create new threads threads = [HttpRequestThread(url) for url in urls] # start the threads [t.start() for t in threads] # wait for the threads to complete [t.join() for t in threads] # display the URLs with HTTP status codes [print(f' : ') for t in threads] if __name__ == '__main__': main() def main() -> None: urls = [ 'https://httpstat.us/200', 'https://httpstat.us/400' ] # create new threads threads = [HttpRequestThread(url) for url in urls] # start the threads [t.start() for t in threads] # wait for the threads to complete [t.join() for t in threads] # display the URLs with HTTP status codes [print(f' : ') for t in threads] Code language: Python (python)
ttps://httpstat.us/200: 200 https://httpstat.us/400: 400Code language: Python (python)

First, define the HttpRequestThread class that extends the Thread class:

class HttpRequestThread(Thread): # . Code language: Python (python)

Second, define the __init__() method that accepts a URL. Inside the __init__() method adds url , http_status_code , and reason instance variables. The http_status_code will store the status code of the url at the time of checking and the reason will store the error message in case an error occurs:

def __init__(self, url: str) -> None: super().__init__() self.url = url self.http_status_code = None self.reason = NoneCode language: Python (python)

Third, override the run() method that uses the urllib library to get the HTTP status code of the specified URL and assigns it to http_status_code field. If an error occurs, it assigns the error message to the reason field:

def run(self) -> None: try: response = urllib.request.urlopen(self.url) self.http_status_code = response.code except urllib.error.HTTPError as e: self.http_status_code = e.code except urllib.error.URLError as e: self.reason = e.reasonCode language: Python (python)

Later, we can access the url , http_status_code , and reason from the instance of the HttpRequestThread class.

Fourth, define the main() function that creates instances of the HttpRequestThread class, start the threads, wait for them to complete, and get the result from the instances:

def main() -> None: urls = [ 'https://httpstat.us/200', 'https://httpstat.us/400' ] # create new threads threads = [HttpRequestThread(url) for url in urls] # start the threads [t.start() for t in threads] # wait for the threads to complete [t.join() for t in threads] # display the URLs with HTTP status codes [print(f' : ') for t in threads]Code language: Python (python)

Summary

  • Extend the Thread class and set the instance variables inside the subclass to return the values from a child thread to the main thread.

Источник

How to return a result from a Python thread

Suppose you have a Python thread that runs your target function.

  • Simple scenario: That target function returns a result that you want to retrieve.
  • A more advanced scenario: You want to retrieve the result of the target function if the thread does not time out.

There are several ways to retrieve a value from a Python thread. You can use concurrent.futures , multiprocessing.pool.ThreadPool or just threading with Queue .

This post proposes an alternative solution that does not require any other package aside from threading .

The solution

If you don’t want to use anything else beside the threading module, the solution is simple:

  1. Extend the threading.Thread class and add a result member to your new class. Make sure to take into account positional and keyword arguments in the constructor.
  2. Override the base class’s run() method: in addition to running the target function as expected (with its args and kwargs intact), it has to store the target’s result in the new member result .
  3. Override the base class’s join() method: with args and kwargs intact, simply join() as in the base class but also return the result.
  4. Then when you instantiate your new thread class, intercept the result returned by join() .

Note the stress placed upon preserving the target’s positional and keyword arguments: this ensures that you can also join() the thread with a timeout, as you would a with a threading.Thread instance.

The following section illustrates these steps.

Implementation: ReturnValueThread class

Below, the class ReturnValueThread extends threading.Thread (lines 4-19).

  1. In the constructor, we declare a result member that will store the result returned by the target function (lines 6-8).
  2. We override the run() method by storing the result of the target in the result member (lines 10-16).
  3. We override the join() method such as to return the result member (lines 18-20).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
import threading import sys class ReturnValueThread(threading.Thread): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.result = None def run(self): if self._target is None: return # could alternatively raise an exception, depends on the use case try: self.result = self._target(*self._args, **self._kwargs) except Exception as exc: print(f'type(exc).__name__>: exc>', file=sys.stderr) # properly handle the exception def join(self, *args, **kwargs): super().join(*args, **kwargs) return self.result 

Usage example for ReturnValueThread

Here is how to use the ReturnValueThread class defined above. Imagine that the target functions both compute and return the square of the argument that gets passed to them:

  • square() returns the square of its argument instantly (lines 4-5);
  • think_about_square() returns the square of its argument after having… thought about it for a while (lines 8-10).

Why do we have two target functions in this example? Remember the scenarios mentioned at the beginning of this post:

  1. A simple scenario is to simply retrieve the value returned by the target function (lines 16-19);
  2. A more advanced scenario is to retrieve the value if the function finishes running before a specified timeout (lines 21-27).
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
import time def square(x): return x ** 2 def think_about_square(x): time.sleep(x) return square(x) def main(): value = 3 thread1 = ReturnValueThread(target=square, args=(value,)) thread1.start() result = thread1.join() print(f'square(value>) = result>') thread2 = ReturnValueThread(target=think_about_square, args=(value,)) thread2.start() result = thread2.join(timeout=1) if thread2.is_alive(): print('Timeout in think_about_square') # properly handle timeout else: print(f'think_about_square(value>) = result>') if __name__ == '__main__': main() 

thread1 is the thread running square() (instant result, retrieved as expected). thread2 , on the other hand, runs think_about_square() , and it just so happens that it does not finish within the allotted time. We test whether the thread finished at line 24 via thread2.is_alive() .

Caveat

The more observant types have probably noticed that although ReturnValueThread returns the result of the target function, our thread2 in the above example (the thread that times out) does not exit cleanly. In fact, it runs until the sleep() ends. In a previous post we have seen how to exit a Python thread cleanly. Another solution is to use a process instead of a thread, but this comes with its own set of complications. The most notable difficulty is the fact that, unlike threads, processes run in separate memory spaces, which tends to complicate things since resources now have to be shared.

Further reading

  • How to exit a Python thread cleanly (using a threading event)
  • Multiprocessing in Python with shared resources
  • concurrent.futures (Python documentation)
  • multiprocessing.pool.ThreadPool (Python documentation)
  • threading (Python documentation)
  • Queue (Python documentation)

Источник

Оцените статью