Как получить возвращаемое значение из потока в Python?


342

Функция fooниже возвращает строку 'foo'. Как я могу получить значение'foo' которое возвращается из цели потока?

from threading import Thread

def foo(bar):
    print('hello {}'.format(bar))
    return 'foo'

thread = Thread(target=foo, args=('world!',))
thread.start()
return_value = thread.join()

«Один очевидный способ сделать это», показанный выше, не работает: thread.join()возвращено None.

Ответы:


39

В Python 3.2+ concurrent.futuresмодуль stdlib предоставляет API более высокого уровня threading, включая передачу возвращаемых значений или исключений из рабочего потока обратно в основной поток:

import concurrent.futures

def foo(bar):
    print('hello {}'.format(bar))
    return 'foo'

with concurrent.futures.ThreadPoolExecutor() as executor:
    future = executor.submit(foo, 'world!')
    return_value = future.result()
    print(return_value)

1
Для тех, кому интересно, это можно сделать с помощью списка тем. futures = [executor.submit(foo, param) for param in param_list]Порядок будет поддерживаться, и выход из него withпозволит собирать результаты. [f.result() for f in futures]
jayreed1

273

FWIW, multiprocessingмодуль имеет хороший интерфейс для этого, используя Poolкласс. И если вы хотите придерживаться потоков, а не процессов, вы можете просто использовать multiprocessing.pool.ThreadPoolкласс в качестве замены.

def foo(bar, baz):
  print 'hello {0}'.format(bar)
  return 'foo' + baz

from multiprocessing.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.

50
@JakeBiesinger Я хочу сказать, что я искал ответ, как получить ответ от Thread, пришел сюда, и принятый ответ не отвечает на поставленный вопрос. Я различаю темы и процессы. Я знаю о Global Interpreter Lock, однако я работаю над проблемой ввода-вывода, поэтому потоки в порядке, мне не нужны процессы. Другие ответы здесь лучше ответят на поставленный вопрос.
omikron

7
@omikron Но потоки в python не возвращают ответ, если вы не используете подкласс, который включает эту функцию. Из возможных подклассов ThreadPools - отличный выбор (выберите количество потоков, используйте map / apply w / sync / async). Несмотря на то multiprocess, что они импортированы , они не имеют ничего общего с процессами.
Джейк Бизингер

4
@ JakeBiesinger О, я слепой. Извините за мои ненужные комментарии. Ты прав. Я просто предположил, что многопроцессорность = процессы.
omikron

12
Не забудьте установить processes=1более одного, если у вас есть больше тем!
Иман

4
Проблема с многопроцессорностью и пулом потоков состоит в том, что он намного медленнее настраивает и запускает потоки по сравнению с базовой библиотекой потоков. Это отлично подходит для запуска длинных потоков, но побеждать цель, когда нужно запустить много коротких потоков. По моему мнению, решение использования «многопоточности» и «очереди», описанное в других ответах, является лучшей альтернативой для этого последнего варианта использования.
Ив Дорфсман

243

Один из способов, которые я видел, это передать изменяемый объект, такой как список или словарь, в конструктор потока вместе с индексом или другим идентификатором некоторого вида. Затем поток может сохранить свои результаты в своем выделенном слоте в этом объекте. Например:

def foo(bar, result, index):
    print 'hello {0}'.format(bar)
    result[index] = "foo"

from threading import Thread

threads = [None] * 10
results = [None] * 10

for i in range(len(threads)):
    threads[i] = Thread(target=foo, args=('world!', results, i))
    threads[i].start()

# do some other stuff

for i in range(len(threads)):
    threads[i].join()

print " ".join(results)  # what sound does a metasyntactic locomotive make?

Если вы действительно хотите join()вернуть возвращаемое значение вызванной функции, вы можете сделать это с помощью Threadподкласса, подобного следующему:

from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs, Verbose)
        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):
        Thread.join(self)
        return self._return

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print twrv.join()   # prints foo

Это становится немного странным из-за некоторого искажения имени, и оно получает доступ к «частным» структурам данных, которые специфичны для Threadреализации ... но это работает.

Для python3

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs)
        self._return = None
    def run(self):
        print(type(self._target))
        if self._target is not None:
            self._return = self._target(*self._args,
                                                **self._kwargs)
    def join(self, *args):
        Thread.join(self, *args)
        return self._return

37
круто, спасибо за пример! Интересно, почему Thread не был реализован с обработкой возвращаемого значения, это кажется достаточно очевидной вещью для поддержки.
Вим

16
Я думаю, что это должен быть принятый ответ - запросил OP threading, а не использовать другую библиотеку, плюс ограничение размера пула создает дополнительную потенциальную проблему, которая произошла в моем случае.
Домоаригато

10
Отличная поездная шутка.
Meawoppl

7
На python3 это возвращается TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. Есть ли способ это исправить?
GuySoft

2
Предупреждение для всех, кто испытывает желание сделать второй из этих ( _Thread__targetвещь). Вы заставите любого, кто пытается портировать ваш код на python 3, ненавидеть вас, пока он не отработает то, что вы сделали (из-за использования недокументированных функций, которые изменились между 2 и 3). Хорошо документируйте свой код.
Бен Тейлор

84

Ответ Джейка хорош, но если вы не хотите использовать пул потоков (вы не знаете, сколько потоков вам понадобится, но создаете их по мере необходимости), тогда хорошим способом передачи информации между потоками является встроенный Класс Queue.Queue , так как он обеспечивает безопасность потоков.

Я создал следующий декоратор, чтобы он действовал аналогично пулу потоков:

def threaded(f, daemon=False):
    import Queue

    def wrapped_f(q, *args, **kwargs):
        '''this function calls the decorated function and puts the 
        result in a queue'''
        ret = f(*args, **kwargs)
        q.put(ret)

    def wrap(*args, **kwargs):
        '''this is the function returned from the decorator. It fires off
        wrapped_f in a new thread and returns the thread object with
        the result queue attached'''

        q = Queue.Queue()

        t = threading.Thread(target=wrapped_f, args=(q,)+args, kwargs=kwargs)
        t.daemon = daemon
        t.start()
        t.result_queue = q        
        return t

    return wrap

Тогда вы просто используете его как:

@threaded
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Thread object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result_queue.get()
print result

Декорированная функция создает новый поток при каждом вызове и возвращает объект Thread, содержащий очередь, которая получит результат.

ОБНОВИТЬ

Прошло довольно много времени с тех пор, как я опубликовал этот ответ, но он все еще получает представления, поэтому я подумал, что обновлю его, чтобы отразить способ, которым я делаю это в новых версиях Python:

В concurrent.futuresмодуль добавлен Python 3.2, который обеспечивает высокоуровневый интерфейс для параллельных задач. Это обеспечивает ThreadPoolExecutorиProcessPoolExecutor , таким образом, вы можете использовать пул потоков или процессов с одинаковыми API.

Одним из преимуществ этого API является то, что отправка задачи на ExecutorвозвратFuture объект, который будет дополнен возвращаемым значением вызываемого вами запроса.

Это делает queueненужным прикрепление объекта, что немного упрощает декоратор:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return (executor or _DEFAULT_POOL).submit(f, *args, **kwargs)

    return wrap

Это будет использовать модуль по умолчанию исполнитель пула потоков если он не был передан.

Использование очень похоже на ранее:

@threadpool
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Future object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result()
print result

Если вы используете Python 3.4+, одна действительно хорошая особенность использования этого метода (и объектов Future в целом) заключается в том, что возвращаемое будущее можно обернуть, чтобы превратить его в asyncio.Futurewith asyncio.wrap_future. Это позволяет легко работать с сопрограммами:

result = await asyncio.wrap_future(long_task(10))

Если вам не нужен доступ к базовому concurrent.Futureобъекту, вы можете включить перенос в декоратор:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return asyncio.wrap_future((executor or _DEFAULT_POOL).submit(f, *args, **kwargs))

    return wrap

Затем, когда вам нужно вытолкнуть интенсивный процессор или блокировать код из потока цикла событий, вы можете поместить его в декорированную функцию:

@threadpool
def some_long_calculation():
    ...

# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()

Я не могу заставить это работать; Я получаю сообщение об ошибке, утверждающее, AttributeError: 'module' object has no attribute 'Lock'что оно исходит из строки y = long_task(10)... мысли?
Sadmicrowave

1
Код явно не использует Lock, поэтому проблема может быть где-то еще в вашем коде. Возможно, вы захотите опубликовать новый вопрос об этом
bj0

Почему result_queue является атрибутом экземпляра? Было бы лучше, если бы это был атрибут класса, чтобы пользователи не знали, как вызывать result_queue при использовании @threaded, который не является явным и неоднозначным?
nonbot

@ t88, не уверен, что ты имеешь в виду, тебе нужен какой-то способ доступа к результату, а это значит, что тебе нужно знать, что звонить. Если вы хотите, чтобы это было что-то другое, вы можете создать подкласс Thread и делать то, что вы хотите (это было простое решение). Причина, по которой очередь должна быть присоединена к потоку, заключается в том, что несколько вызовов / функций имеют свои собственные очереди
bj0

1
Это великолепно! Большое спасибо.
Ганеш Катирезан

53

Другое решение, которое не требует изменения существующего кода:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
t.join()
result = que.get()
print result

Его также можно легко настроить для многопоточной среды:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()
threads_list = list()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
threads_list.append(t)

# Add more threads here
...
threads_list.append(t2)
...
threads_list.append(t3)
...

# Join all the threads
for t in threads_list:
    t.join()

# Check thread's return value
while not que.empty():
    result = que.get()
    print result

t = Thread (target = lambda q, arg1: q.put (foo (arg1)), args = (que, 'world!')) что здесь делает q.put, что делает Queue.Queue ()
vijay Shanker

6
В вашем родном городе должна быть ваша статуя, спасибо!
Онилол

3
@Onilol - Большое спасибо. Именно ваш комментарий и является причиной, по которой я это делаю :)
Арик

4
Для Python3 необходимо изменить на from queue import Queue.
Джино Мемпин

1
По-видимому, это наименее разрушительный метод (не нужно кардинально перестраивать исходную базу кода), чтобы возвращаемое значение возвращалось в основной поток.
Фанчен Бао

24

Parris / ответ join / returnответ kindall перенесен на Python 3:

from threading import Thread

def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon=daemon)

        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self):
        Thread.join(self)
        return self._return


twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print(twrv.join())   # prints foo

Обратите внимание, что Threadкласс реализован по-другому в Python 3.


1
join принимает параметр времени ожидания, который должен быть передан
cz

22

Я украл ответ Уиндола и немного его почистил.

Ключевой частью является добавление * args и ** kwargs в join () для обработки времени ожидания

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

ОБНОВЛЕНИЕ ОТВЕТА НИЖЕ

Это мой самый популярный ответ, поэтому я решил обновить код, который будет работать как на py2, так и на py3.

Кроме того, я вижу много ответов на этот вопрос, которые показывают отсутствие понимания в отношении Thread.join (). Некоторые совершенно не справляются с timeoutаргументом. Но есть также угловой случай, о котором вам следует знать в отношении случаев, когда у вас есть (1) целевая функция, которая может возвращать Noneи (2) вы также передаете timeoutаргумент arg для join (). Пожалуйста, смотрите "Тест 4", чтобы понять этот угловой случай.

Класс ThreadWithReturn, который работает с py2 и py3:

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

if sys.version_info >= (3, 0):
    _thread_target_key = '_target'
    _thread_args_key = '_args'
    _thread_kwargs_key = '_kwargs'
else:
    _thread_target_key = '_Thread__target'
    _thread_args_key = '_Thread__args'
    _thread_kwargs_key = '_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 not target is 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

Некоторые примеры тестов приведены ниже:

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))

Можете ли вы определить угловой случай, с которым мы можем столкнуться с ТЕСТОМ 4?

Проблема в том, что мы ожидаем, что метод giveMe () вернет None (см. ТЕСТ 2), но мы также ожидаем, что join () вернет None, если время ожидания истекло.

returned is None означает либо:

(1) это то, что вернул giveMe (), или

(2) истекло время соединения ()

Этот пример тривиален, так как мы знаем, что метод giveMe () всегда будет возвращать None. Но в случае реального мира (где цель может законно вернуть None или что-то еще), мы бы хотели явно проверить, что произошло.

Ниже описано, как решить этот угловой случай:

# 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()

Знаете ли вы эквивалент _Thread_target для Python3? Этот атрибут не существует в Python3.
GreySage

Я посмотрел в файле threading.py, оказалось, что это _target (другие атрибуты имеют аналогичные имена).
GreySage

Вы могли бы избежать доступа к закрытым переменным класса потоков, если вы сохраните target, argsи kwargsаргументы для инициализации как переменные в вашем классе.
Толли

@GreySage Посмотрите мой ответ, я портировал этот блок на python3 ниже
GuySoft

Ответ @GreySage теперь поддерживает py2 и py3
user2426679

15

Использование очереди:

import threading, queue

def calc_square(num, out_queue1):
  l = []
  for x in num:
    l.append(x*x)
  out_queue1.put(l)


arr = [1,2,3,4,5,6,7,8,9,10]
out_queue1=queue.Queue()
t1=threading.Thread(target=calc_square, args=(arr,out_queue1))
t1.start()
t1.join()
print (out_queue1.get())

1
Очень нравится это решение, короткое и сладкое. Если ваша функция считывает входную очередь, и вы добавите к out_queue1вам нужно перебрать out_queue1.get()и поймать исключение Queue.Empty: ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. Точки с запятой для имитации разрывов строк.
sastorsl

6

Мое решение проблемы состоит в том, чтобы обернуть функцию и поток в классе. Не требует использования пулов, очередей или передачи переменных типа c. Это также не блокирует. Вместо этого вы проверяете статус. Смотрите пример того, как использовать его в конце кода.

import threading

class ThreadWorker():
    '''
    The basic idea is given a function create an object.
    The object can then run the function in a thread.
    It provides a wrapper to start it,check its status,and get data out the function.
    '''
    def __init__(self,func):
        self.thread = None
        self.data = None
        self.func = self.save_data(func)

    def save_data(self,func):
        '''modify function to save its returned data'''
        def new_func(*args, **kwargs):
            self.data=func(*args, **kwargs)

        return new_func

    def start(self,params):
        self.data = None
        if self.thread is not None:
            if self.thread.isAlive():
                return 'running' #could raise exception here

        #unless thread exists and is alive start or restart it
        self.thread = threading.Thread(target=self.func,args=params)
        self.thread.start()
        return 'started'

    def status(self):
        if self.thread is None:
            return 'not_started'
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return 'finished'

    def get_results(self):
        if self.thread is None:
            return 'not_started' #could return exception
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return self.data

def add(x,y):
    return x +y

add_worker = ThreadWorker(add)
print add_worker.start((1,2,))
print add_worker.status()
print add_worker.get_results()

как бы вы справились с исключением? допустим, была добавлена ​​функция add и int и str. все потоки потерпят неудачу или только один потерпит неудачу?
user1745713

4

joinвсегда возвращаться None, я думаю, что вы должны иметь подкласс Threadдля обработки кодов возврата и так далее.


4

Принимая во внимание @iman комментария на @JakeBiesinger ответ я воссозданный его иметь различное количество потоков:

from multiprocessing.pool import ThreadPool

def foo(bar, baz):
    print 'hello {0}'.format(bar)
    return 'foo' + baz

numOfThreads = 3 
results = []

pool = ThreadPool(numOfThreads)

for i in range(0, numOfThreads):
    results.append(pool.apply_async(foo, ('world', 'foo'))) # tuple of args for foo)

# do some other stuff in the main process
# ...
# ...

results = [r.get() for r in results]
print results

pool.close()
pool.join()

Ура,

Guy.


2

Вы можете определить изменяемую область выше области действия многопоточной функции и добавить к ней результат. (Я также изменил код для совместимости с python3)

returns = {}
def foo(bar):
    print('hello {0}'.format(bar))
    returns[bar] = 'foo'

from threading import Thread
t = Thread(target=foo, args=('world!',))
t.start()
t.join()
print(returns)

Это возвращает {'world!': 'foo'}

Если вы используете функцию ввода как ключ к вашим результатам, каждый уникальный вход гарантированно даст запись в результатах.


2

Я использую эту обертку, которая удобно превращает любую функцию для запуска Thread- заботясь о ее возвращаемом значении или исключении. Это не добавляет Queueнакладных расходов.

def threading_func(f):
    """Decorator for running a function in a thread and handling its return
    value or exception"""
    def start(*args, **kw):
        def run():
            try:
                th.ret = f(*args, **kw)
            except:
                th.exc = sys.exc_info()
        def get(timeout=None):
            th.join(timeout)
            if th.exc:
                raise th.exc[0], th.exc[1], th.exc[2] # py2
                ##raise th.exc[1] #py3                
            return th.ret
        th = threading.Thread(None, run)
        th.exc = None
        th.get = get
        th.start()
        return th
    return start

Примеры использования

def f(x):
    return 2.5 * x
th = threading_func(f)(4)
print("still running?:", th.is_alive())
print("result:", th.get(timeout=1.0))

@threading_func
def th_mul(a, b):
    return a * b
th = th_mul("text", 2.5)

try:
    print(th.get())
except TypeError:
    print("exception thrown ok.")

Примечания к threadingмодулю

Удобное возвращаемое значение и обработка исключений для многопоточной функции - это частая «Pythonic» потребность, которая должна уже предлагаться threadingмодулем - возможно, непосредственно в стандартном Threadклассе. ThreadPoolимеет слишком много накладных расходов для простых задач - 3 управления потоками, много бюрократии. К сожалению Thread, макет изначально был скопирован с Java - что вы видите, например, из все еще бесполезного первого (!) Параметра конструктора group.


первый конструктор не бесполезен, он зарезервирован для будущей реализации .. из кулинарной книги по параллельному программированию на python
Виджай Шенкер

1

Определите вашу цель:
1) принять аргумент q
2) заменить любые утверждения return fooнаq.put(foo); return

так что функция

def func(a):
    ans = a * a
    return ans

станет

def func(a, q):
    ans = a * a
    q.put(ans)
    return

и тогда вы будете действовать как таковой

from Queue import Queue
from threading import Thread

ans_q = Queue()
arg_tups = [(i, ans_q) for i in xrange(10)]

threads = [Thread(target=func, args=arg_tup) for arg_tup in arg_tups]
_ = [t.start() for t in threads]
_ = [t.join() for t in threads]
results = [q.get() for _ in xrange(len(threads))]

И вы можете использовать функциональные декораторы / обертки, чтобы сделать это так, чтобы вы могли использовать ваши существующие функции как targetбез их изменения, но следуйте этой базовой схеме.


Это должно бытьresults = [ans_q.get() for _ in xrange(len(threads))]
Hemant H Kumar

1

Как уже упоминалось, многопроцессорный пул намного медленнее, чем базовые потоки. Использование очередей, предложенных в некоторых ответах, является очень эффективной альтернативой. Я использовал его со словарями, чтобы иметь возможность запускать множество небольших потоков и восстанавливать несколько ответов, комбинируя их со словарями:

#!/usr/bin/env python3

import threading
# use Queue for python2
import queue
import random

LETTERS = 'abcdefghijklmnopqrstuvwxyz'
LETTERS = [ x for x in LETTERS ]

NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

def randoms(k, q):
    result = dict()
    result['letter'] = random.choice(LETTERS)
    result['number'] = random.choice(NUMBERS)
    q.put({k: result})

threads = list()
q = queue.Queue()
results = dict()

for name in ('alpha', 'oscar', 'yankee',):
    threads.append( threading.Thread(target=randoms, args=(name, q)) )
    threads[-1].start()
_ = [ t.join() for t in threads ]
while not q.empty():
    results.update(q.get())

print(results)

1

Идея GuySoft великолепна, но я думаю, что объект не обязательно должен наследоваться от Thread и start () может быть удален из интерфейса:

from threading import Thread
import queue
class ThreadWithReturnValue(object):
    def __init__(self, target=None, args=(), **kwargs):
        self._que = queue.Queue()
        self._t = Thread(target=lambda q,arg1,kwargs1: q.put(target(*arg1, **kwargs1)) ,
                args=(self._que, args, kwargs), )
        self._t.start()

    def join(self):
        self._t.join()
        return self._que.get()


def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

print(twrv.join())   # prints foo

0

Одно из обычных решений - обернуть вашу функцию fooдекоратором, например

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

Тогда весь код может выглядеть так

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]

for t in threads:
    t.start()
    while(True):
        if(len(threading.enumerate()) < max_num):
            break
for t in threads:
    t.join()
return result

Заметка

Одна важная проблема заключается в том, что возвращаемые значения могут быть неупорядоченными . (На самом деле, return valueне обязательно сохраняется в queue, так как вы можете выбрать произвольную потокобезопасную структуру данных)


0

Почему бы просто не использовать глобальную переменную?

import threading


class myThread(threading.Thread):
    def __init__(self, ind, lock):
        threading.Thread.__init__(self)
        self.ind = ind
        self.lock = lock

    def run(self):
        global results
        with self.lock:
            results.append(self.ind)



results = []
lock = threading.Lock()
threads = [myThread(x, lock) for x in range(1, 4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(results)

0

Ответ Киндалла в Python3

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon)
        self._return = None 

    def run(self):
        try:
            if self._target:
                self._return = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs 

    def join(self,timeout=None):
        Thread.join(self,timeout)
        return self._return

-2

Если из вызова функции нужно проверить только True или False, я нашел бы более простое решение - обновить глобальный список.

import threading

lists = {"A":"True", "B":"True"}

def myfunc(name: str, mylist):
    for i in mylist:
        if i == 31:
            lists[name] = "False"
            return False
        else:
            print("name {} : {}".format(name, i))

t1 = threading.Thread(target=myfunc, args=("A", [1, 2, 3, 4, 5, 6], ))
t2 = threading.Thread(target=myfunc, args=("B", [11, 21, 31, 41, 51, 61], ))
t1.start()
t2.start()
t1.join()
t2.join()

for value in lists.values():
    if value == False:
        # Something is suspicious 
        # Take necessary action 

Это более полезно, если вы хотите узнать, вернул ли какой-либо из потоков ложное состояние для выполнения необходимых действий.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.