Як отримати повернене значення з потоку в 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.

Відповіді:


37

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

Для тих, хто цікавиться, це можна зробити зі списком тем. 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 Моя думка полягає в тому, що я шукав відповідь, як отримати відповідь від теми, прийшов сюди, і прийнята відповідь не відповідає на вказане питання. Я розмежую нитки та процеси. Я знаю про Global Interpreter Lock, однак я працюю над проблемою, пов’язаною з входом / виводом, тому нитки в порядку, мені не потрібні процеси. Інші відповіді тут краще відповіді на запитання.
омікрон

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

4
@JakeBiesinger О, я сліпий. Вибачте за мої зайві коментарі. Ти правий. Я просто припустив, що багатопроцесорна = процеси.
омікрон

12
Не забудьте встановити processes=1більше одного, якщо у вас є більше ниток!
iman

4
Проблема багатопроцесорної обробки та пулу потоків полягає в тому, що вона набагато повільніше налаштовує та запускає потоки порівняно з базовою бібліотекою ниток. Він чудово підходить для запуску довгих ниток, але перемагає мету, коли потрібно запускати багато коротких потоків. На мою думку, рішення про використання "нитки" та "черги", зафіксованого в інших відповідях, є кращою альтернативою для останнього випадку використання.
Ів Дорфсман

242

Один із способів, який я бачив, - це передавати змінений об’єкт, такий як список чи словник, до конструктора потоку разом із індексом чи іншим ідентифікатором якогось типу. Потім нитка може зберігати свої результати у виділеному слоті в цьому об'єкті. Наприклад:

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
Я думаю, що це має бути прийнятою відповіддю - ОП попросили threading, а не інша бібліотека, щоб спробувати, плюс обмеження розміру пулу вводить додаткову потенційну проблему, яка трапилася в моєму випадку.
domoarigato

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

Відповідь Джейка хороша, але якщо ви не хочете використовувати нитку потоків (ви не знаєте, скільки ниток вам знадобиться, але створіть їх у міру необхідності), тоді хороший спосіб передачі інформації між потоками - це вбудований Черга. Черга class, оскільки він забезпечує безпеку ниток.

Я створив наступного декоратора, щоб змусити його діяти подібним чином до нитки:

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.

Однією з переваг цієї програми є те, що подання завдання 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+, один дуже добра ознака використання цього методу (і майбутні об'єкти в цілому) є те , що повертається майбутнє може бути обгорнуте , щоб перетворити його в asyncio.Futureс 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 є атрибутом екземпляра? Було б краще, якщо б це був атрибут класу, щоб користувачі не знали дзвонити результат_queue при використанні @threaded, що не є явним і неоднозначним?
nonbot

@ t88, не впевнений, що ти маєш на увазі, потрібен певний спосіб доступу до результату, а це означає, що ти повинен знати, як зателефонувати. Якщо ви хочете, щоб це було щось інше, ви можете підкласировать тему і робити все, що вам завгодно (це було просте рішення). Причина того, що чергу потрібно приєднувати до потоку, полягає в тому, що у кількох викликів / функцій є свої черги
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!')) Whats q.put тут робиться, що робить Queue.Queue ()
vijay shanker

6
У вашому рідному місті має бути статуя, дякую!
Онилол

3
@Onilol - Дуже дякую Ваш коментар - саме тому я і роблю це :)
Арік

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

1
Це здається найменш руйнівним методом (не потрібно різко реструктурувати вихідну базу коду), щоб повернути значення, що повертається до основного потоку.
Fanchen Bao

24

Відповідь 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 для приєднання (), щоб обробити час очікування

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 передаєте аргумент для приєднання (). Будь ласка, дивіться "TEST 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))

Чи можете ви визначити кутовий випадок, який ми можемо зіткнутися з TEST 4?

Проблема полягає в тому, що ми очікуємо, що GiveMe () поверне None (див. TEST 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. чи всі нитки провалилися б, чи тільки одна не вийшла?
користувач1745713

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

Ура,

Хлопець.


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модуля

Зручне повернення величини та обробка винятків функцією різьблення є частою "пітонічною" потребою, і вона вже повинна бути запропонована threadingмодулем - можливо, безпосередньо в стандартному Threadкласі. ThreadPoolмає занадто багато накладних витрат для простих завдань - 3 керування нитками, багато бюрократії. На жаль Thread, макет був скопійований з Java спочатку - що ви бачите, наприклад, із ще непридатного першого (!) Параметра конструктора group.


перший конструктор не марний, він зарезервований там для подальшої реалізації .. з python-паралельної кулінарної програми програмування
vijay shanker

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))]
Кумар

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.