下面的函数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。


当前回答

您可以在线程函数的作用域之上定义一个可变变量,并将结果添加到该变量中。(我还修改了代码,使其与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”}

如果使用函数input作为结果字典的键,则保证每个惟一的输入都在结果中给出一个条目

其他回答

Kindall在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

在Python 3.2+中,stdlib concurrent。futures模块为线程提供了一个更高级别的API,包括将返回值或异常从工作线程传递回主线程:

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)

我找到的大多数答案都很长,需要熟悉其他模块或高级python特性,除非他们已经熟悉答案所谈论的一切,否则会让人感到困惑。

简化方法的工作代码:

import threading

class ThreadWithResult(threading.Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
        def function():
            self.result = target(*args, **kwargs)
        super().__init__(group=group, target=function, name=name, daemon=daemon)

示例代码:

import time, random


def function_to_thread(n):
    count = 0
    while count < 3:
            print(f'still running thread {n}')
            count +=1
            time.sleep(3)
    result = random.random()
    print(f'Return value of thread {n} should be: {result}')
    return result


def main():
    thread1 = ThreadWithResult(target=function_to_thread, args=(1,))
    thread2 = ThreadWithResult(target=function_to_thread, args=(2,))
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print(thread1.result)
    print(thread2.result)

main()

解释: 我想大大简化事情,所以我创建了一个ThreadWithResult类,并让它继承threading.Thread。__init__中的嵌套函数函数调用我们想要保存值的线程函数,并将该嵌套函数的结果保存为实例属性self。线程执行完成后的结果。

创建this的实例与创建threading.Thread的实例是相同的。将希望在新线程上运行的函数传递给目标参数,将函数可能需要的任何参数传递给args参数,将任何关键字参数传递给kwargs参数。

e.g.

my_thread = ThreadWithResult(target=my_function, args=(arg1, arg2, arg3))

我认为这比绝大多数答案更容易理解,而且这种方法不需要额外的导入!我加入了time和random模块来模拟线程的行为,但它们并不是实现最初问题中所要求的功能所必需的。

我知道我是在这个问题被问到很久之后才回答的,但我希望这能在未来帮助更多的人!


编辑:我创建了保存线程结果的PyPI包,允许你访问上面相同的代码,并在项目中重用它(GitHub代码在这里)。PyPI包完全扩展了线程。线程类,因此您可以设置在线程上设置的任何属性。线程在ThreadWithResult类!

上面的原始答案介绍了这个子类背后的主要思想,但要了解更多信息,请参阅这里更详细的解释(来自模块docstring)。

快速使用示例:

pip3 install -U save-thread-result     # MacOS/Linux
pip  install -U save-thread-result     # Windows

python3     # MacOS/Linux
python      # Windows
from save_thread_result import ThreadWithResult

# As of Release 0.0.3, you can also specify values for
#`group`, `name`, and `daemon` if you want to set those
# values manually.
thread = ThreadWithResult(
    target = my_function,
    args   = (my_function_arg1, my_function_arg2, ...)
    kwargs = {my_function_kwarg1: kwarg1_value, my_function_kwarg2: kwarg2_value, ...}
)

thread.start()
thread.join()
if getattr(thread, 'result', None):
    print(thread.result)
else:
    # thread.result attribute not set - something caused
    # the thread to terminate BEFORE the thread finished
    # executing the function passed in through the
    # `target` argument
    print('ERROR! Something went wrong while executing this thread, and the function you passed in did NOT complete!!')

# seeing help about the class and information about the threading.Thread super class methods and attributes available:
help(ThreadWithResult)

一种常见的解决方案是用装饰器来包装函数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

Note

一个重要的问题是返回值可能是无序的。 (事实上,返回值不一定保存到队列中,因为您可以选择任意线程安全的数据结构)

我对这个问题的解决方案是将函数和线程包装在一个类中。不需要使用池、队列或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()