Python threading возврат значения

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)

Источник

Вернуть значение из потока в python 3?

Собственно вопрос. Потоки в питоне работают шустро, но вот как нормально вернуть значение из него я не нашел. Вот пример кода:

import requests import random import re import queue pattern = 'http://www.astateoftrance.com/episodes/episode-' k = 700 lock = threading.Lock() def getPage(): #print ("Hello") global k while (k < 800): url = pattern+str(k)+'/' response = requests.get(url) content = response.content.decode('utf-8') contLow = content.lower() #print(contLow) if (re.findall('', contLow)): print(url) lock.acquire() k = k + 1 lock.release() threads = [] for i in range(20): t = threading.Thread(target=getPage) threads.append(t) t.start()

Где-то предлагают использовать вместо threading multiprocessing, кто-то советует переопределить класс потока так чтобы он возвращал значение. Разве нет более прямого пути получить из потока значение? Или нужно обязательно использовать вспомагательное решение?

Это типа комплимент? Потоки в питоне, как и сам питон, медленные. А ещё и GIL есть.

Значение ты можешь записывать в threadsafe структуры. Например, в список, а в главном потоке проверять, достаточной ли длины он и если нет - спать 0.1 секунду. Не забудь только отлавливать таймауты и ошибки.

P.S. То, чего ты хочешь добиться удобно делается через aiohttp / eventlet

В сравнении с тем же пхп, по ощущению заметно быстрее. А разве нету "родного" решения? Еще нашел через queue, но скорость исполнения в разы меньше.

Обычно потоки не возвращают значений, а записывают их в list или куда нибудь ещё.
Я бы ваш пример написал так(кстати в results находятся все скачанные страницы):

from multiprocessing.dummy import Pool as ThreadPool from pprint import pprint import requests pattern = 'http://www.astateoftrance.com/episodes/episode-' start_url = 700 urls_list = [] def gen_urls(start): for i in range(start, 800): url = pattern + str(i) + '/' urls_list.append(url) def my_url_get(url): result = requests.get(url) print(" was Downloaded".format(url=url)) return result gen_urls(start_url) pprint(urls_list) pool = ThreadPool(20) results = pool.map(my_url_get, urls_list) pool.close() pool.join() pprint(results[0].content.decode('utf-8'))

Спасибо, хороший пример. Я когда пробовал через multiprocessing с помощью map создавать потоки, то мне какие-то spawn error исключения вылетали. Вообще я с темой мп плохо знаком, только по библиотеке pthreads в php. Там все выглядит проще, но вот стабильности работы нет, что-то постоянно крашится и вылетает и автор довольно лениво отвечает на все запросы на гитхабе.

Войдите, чтобы написать ответ

Отправляются ли вместе с API запросом данные о местоположении?

Источник

Читайте также:  New captcha in php
Оцените статью