Функция 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
.
futures = [executor.submit(foo, param) for param in param_list]
Порядок будет поддерживаться, и выход из негоwith
позволит собирать результаты.[f.result() for f in futures]
FWIW,
multiprocessing
модуль имеет хороший интерфейс для этого, используяPool
класс. И если вы хотите придерживаться потоков, а не процессов, вы можете просто использоватьmultiprocessing.pool.ThreadPool
класс в качестве замены.источник
multiprocess
, что они импортированы , они не имеют ничего общего с процессами.processes=1
более одного, если у вас есть больше тем!Один из способов, которые я видел, это передать изменяемый объект, такой как список или словарь, в конструктор потока вместе с индексом или другим идентификатором некоторого вида. Затем поток может сохранить свои результаты в своем выделенном слоте в этом объекте. Например:
Если вы действительно хотите
join()
вернуть возвращаемое значение вызванной функции, вы можете сделать это с помощьюThread
подкласса, подобного следующему:Это становится немного странным из-за некоторого искажения имени, и оно получает доступ к «частным» структурам данных, которые специфичны для
Thread
реализации ... но это работает.Для python3
источник
threading
, а не использовать другую библиотеку, плюс ограничение размера пула создает дополнительную потенциальную проблему, которая произошла в моем случае.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Есть ли способ это исправить?_Thread__target
вещь). Вы заставите любого, кто пытается портировать ваш код на python 3, ненавидеть вас, пока он не отработает то, что вы сделали (из-за использования недокументированных функций, которые изменились между 2 и 3). Хорошо документируйте свой код.Ответ Джейка хорош, но если вы не хотите использовать пул потоков (вы не знаете, сколько потоков вам понадобится, но создаете их по мере необходимости), тогда хорошим способом передачи информации между потоками является встроенный Класс Queue.Queue , так как он обеспечивает безопасность потоков.
Я создал следующий декоратор, чтобы он действовал аналогично пулу потоков:
Тогда вы просто используете его как:
Декорированная функция создает новый поток при каждом вызове и возвращает объект Thread, содержащий очередь, которая получит результат.
ОБНОВИТЬ
Прошло довольно много времени с тех пор, как я опубликовал этот ответ, но он все еще получает представления, поэтому я подумал, что обновлю его, чтобы отразить способ, которым я делаю это в новых версиях Python:
В
concurrent.futures
модуль добавлен Python 3.2, который обеспечивает высокоуровневый интерфейс для параллельных задач. Это обеспечиваетThreadPoolExecutor
иProcessPoolExecutor
, таким образом, вы можете использовать пул потоков или процессов с одинаковыми API.Одним из преимуществ этого API является то, что отправка задачи на
Executor
возвратFuture
объект, который будет дополнен возвращаемым значением вызываемого вами запроса.Это делает
queue
ненужным прикрепление объекта, что немного упрощает декоратор:Это будет использовать модуль по умолчанию исполнитель пула потоков если он не был передан.
Использование очень похоже на ранее:
Если вы используете Python 3.4+, одна действительно хорошая особенность использования этого метода (и объектов Future в целом) заключается в том, что возвращаемое будущее можно обернуть, чтобы превратить его в
asyncio.Future
withasyncio.wrap_future
. Это позволяет легко работать с сопрограммами:Если вам не нужен доступ к базовому
concurrent.Future
объекту, вы можете включить перенос в декоратор:Затем, когда вам нужно вытолкнуть интенсивный процессор или блокировать код из потока цикла событий, вы можете поместить его в декорированную функцию:
источник
AttributeError: 'module' object has no attribute 'Lock'
что оно исходит из строкиy = long_task(10)
... мысли?Другое решение, которое не требует изменения существующего кода:
Его также можно легко настроить для многопоточной среды:
источник
from queue import Queue
.Parris / ответ
join
/return
ответ kindall перенесен на Python 3:Обратите внимание, что
Thread
класс реализован по-другому в Python 3.источник
Я украл ответ Уиндола и немного его почистил.
Ключевой частью является добавление * args и ** kwargs в join () для обработки времени ожидания
ОБНОВЛЕНИЕ ОТВЕТА НИЖЕ
Это мой самый популярный ответ, поэтому я решил обновить код, который будет работать как на py2, так и на py3.
Кроме того, я вижу много ответов на этот вопрос, которые показывают отсутствие понимания в отношении Thread.join (). Некоторые совершенно не справляются с
timeout
аргументом. Но есть также угловой случай, о котором вам следует знать в отношении случаев, когда у вас есть (1) целевая функция, которая может возвращатьNone
и (2) вы также передаетеtimeout
аргумент arg для join (). Пожалуйста, смотрите "Тест 4", чтобы понять этот угловой случай.Класс ThreadWithReturn, который работает с py2 и py3:
Некоторые примеры тестов приведены ниже:
Можете ли вы определить угловой случай, с которым мы можем столкнуться с ТЕСТОМ 4?
Проблема в том, что мы ожидаем, что метод giveMe () вернет None (см. ТЕСТ 2), но мы также ожидаем, что join () вернет None, если время ожидания истекло.
returned is None
означает либо:(1) это то, что вернул giveMe (), или
(2) истекло время соединения ()
Этот пример тривиален, так как мы знаем, что метод giveMe () всегда будет возвращать None. Но в случае реального мира (где цель может законно вернуть None или что-то еще), мы бы хотели явно проверить, что произошло.
Ниже описано, как решить этот угловой случай:
источник
target
,args
иkwargs
аргументы для инициализации как переменные в вашем классе.Использование очереди:
источник
out_queue1
вам нужно перебратьout_queue1.get()
и поймать исключение Queue.Empty:ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Точки с запятой для имитации разрывов строк.Мое решение проблемы состоит в том, чтобы обернуть функцию и поток в классе. Не требует использования пулов, очередей или передачи переменных типа c. Это также не блокирует. Вместо этого вы проверяете статус. Смотрите пример того, как использовать его в конце кода.
источник
join
всегда возвращатьсяNone
, я думаю, что вы должны иметь подклассThread
для обработки кодов возврата и так далее.источник
Принимая во внимание @iman комментария на @JakeBiesinger ответ я воссозданный его иметь различное количество потоков:
Ура,
Guy.
источник
Вы можете определить изменяемую область выше области действия многопоточной функции и добавить к ней результат. (Я также изменил код для совместимости с python3)
Это возвращает
{'world!': 'foo'}
Если вы используете функцию ввода как ключ к вашим результатам, каждый уникальный вход гарантированно даст запись в результатах.
источник
Я использую эту обертку, которая удобно превращает любую функцию для запуска
Thread
- заботясь о ее возвращаемом значении или исключении. Это не добавляетQueue
накладных расходов.Примеры использования
Примечания к
threading
модулюУдобное возвращаемое значение и обработка исключений для многопоточной функции - это частая «Pythonic» потребность, которая должна уже предлагаться
threading
модулем - возможно, непосредственно в стандартномThread
классе.ThreadPool
имеет слишком много накладных расходов для простых задач - 3 управления потоками, много бюрократии. К сожалениюThread
, макет изначально был скопирован с Java - что вы видите, например, из все еще бесполезного первого (!) Параметра конструктораgroup
.источник
Определите вашу цель:
1) принять аргумент
q
2) заменить любые утверждения
return foo
наq.put(foo); return
так что функция
станет
и тогда вы будете действовать как таковой
И вы можете использовать функциональные декораторы / обертки, чтобы сделать это так, чтобы вы могли использовать ваши существующие функции как
target
без их изменения, но следуйте этой базовой схеме.источник
results = [ans_q.get() for _ in xrange(len(threads))]
Как уже упоминалось, многопроцессорный пул намного медленнее, чем базовые потоки. Использование очередей, предложенных в некоторых ответах, является очень эффективной альтернативой. Я использовал его со словарями, чтобы иметь возможность запускать множество небольших потоков и восстанавливать несколько ответов, комбинируя их со словарями:
источник
Идея GuySoft великолепна, но я думаю, что объект не обязательно должен наследоваться от Thread и start () может быть удален из интерфейса:
источник
Одно из обычных решений - обернуть вашу функцию
foo
декоратором, напримерТогда весь код может выглядеть так
Заметка
Одна важная проблема заключается в том, что возвращаемые значения могут быть неупорядоченными . (На самом деле,
return value
не обязательно сохраняется вqueue
, так как вы можете выбрать произвольную потокобезопасную структуру данных)источник
Почему бы просто не использовать глобальную переменную?
источник
Ответ Киндалла в Python3
источник
Если из вызова функции нужно проверить только True или False, я нашел бы более простое решение - обновить глобальный список.
Это более полезно, если вы хотите узнать, вернул ли какой-либо из потоков ложное состояние для выполнения необходимых действий.
источник