简体   繁体   English

如何获取线程的返回值?

[英]How to get the return value from a thread?

The function foo below returns a string 'foo' .下面的 function foo返回一个字符串'foo' How can I get the value 'foo' which is returned from the thread's target?如何获取从线程目标返回的值'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()

The "one obvious way to do it", shown above, doesn't work: thread.join() returned None .上面显示的“一种明显的方法”不起作用: thread.join()返回None

One way I've seen is to pass a mutable object, such as a list or a dictionary, to the thread's constructor, along with a an index or other identifier of some sort.我见过的一种方法是将可变对象(例如列表或字典)与索引或其他某种标识符一起传递给线程的构造函数。 The thread can then store its results in its dedicated slot in that object.然后线程可以将其结果存储在该对象的专用槽中。 For example:例如:

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?

If you really want join() to return the return value of the called function, you can do this with a Thread subclass like the following:如果你真的希望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

That gets a little hairy because of some name mangling, and it accesses "private" data structures that are specific to Thread implementation... but it works.由于某些名称修改,这有点麻烦,并且它访问特定于Thread实现的“私有”数据结构......但它可以工作。

For python3对于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

FWIW, the multiprocessing module has a nice interface for this using the Pool class. FWIW, multiprocessing模块使用Pool类为此提供了一个很好的接口。 And if you want to stick with threads rather than processes, you can just use the multiprocessing.pool.ThreadPool class as a drop-in replacement.如果你想坚持使用线程而不是进程,你可以使用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.

In Python 3.2+, stdlib concurrent.futures module provides a higher level API to threading , including passing return values or exceptions from a worker thread back to the main thread:在 Python 3.2+ 中,stdlib concurrent.futures模块为threading提供了更高级别的 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)

Jake's answer is good, but if you don't want to use a threadpool (you don't know how many threads you'll need, but create them as needed) then a good way to transmit information between threads is the built-in Queue.Queue class, as it offers thread safety. Jake 的回答很好,但是如果您不想使用线程池(您不知道需要多少线程,但根据需要创建它们),那么在线程之间传输信息的好方法是内置的Queue.Queue类,因为它提供线程安全。

I created the following decorator to make it act in a similar fashion to the threadpool:我创建了以下装饰器以使其以与线程池类似的方式运行:

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

Then you just use it as:然后,您只需将其用作:

@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

The decorated function creates a new thread each time it's called and returns a Thread object that contains the queue that will receive the result.装饰函数每次调用时都会创建一个新线程,并返回一个 Thread 对象,该对象包含将接收结果的队列。

UPDATE更新

It's been quite a while since I posted this answer, but it still gets views so I thought I would update it to reflect the way I do this in newer versions of Python:自从我发布这个答案已经有一段时间了,但它仍然获得了意见,所以我想我会更新它以反映我在较新版本的 Python 中执行此操作的方式:

Python 3.2 added in theconcurrent.futures module which provides a high-level interface for parallel tasks.concurrent.futures模块中添加了 Python 3.2,该模块为并行任务提供了高级接口。 It provides ThreadPoolExecutor and ProcessPoolExecutor , so you can use a thread or process pool with the same api.它提供了ThreadPoolExecutorProcessPoolExecutor ,因此您可以使用具有相同 api 的线程或进程池。

One benefit of this api is that submitting a task to an Executor returns a Future object, which will complete with the return value of the callable you submit.此 api 的一个好处是向Executor提交任务会返回一个Future对象,该对象将以您提交的可调用对象的返回值完成。

This makes attaching a queue object unnecessary, which simplifies the decorator quite a bit:这使得附加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

This will use a default module threadpool executor if one is not passed in.如果未传入,这将使用默认模块线程池执行程序。

The usage is very similar to before:用法和之前很相似:

@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

If you're using Python 3.4+, one really nice feature of using this method (and Future objects in general) is that the returned future can be wrapped to turn it into an asyncio.Future with asyncio.wrap_future .如果您使用的是 Python 3.4+,那么使用此方法(以及一般的 Future 对象)的一个非常好的特性是可以将返回的 future 包装成带有asyncio.Futureasyncio.wrap_future This makes it work easily with coroutines:这使得它可以轻松地与协程一起工作:

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

If you don't need access to the underlying concurrent.Future object, you can include the wrap in the decorator:如果您不需要访问底层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

Then, whenever you need to push cpu intensive or blocking code off the event loop thread, you can put it in a decorated function:然后,每当您需要将 cpu 密集型或阻塞代码从事件循环线程中推送出去时,您可以将其放入修饰函数中:

@threadpool
def some_long_calculation():
    ...

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

Another solution that doesn't require changing your existing code:另一个不需要更改现有代码的解决方案:

import Queue             # Python 2.x
#from queue import Queue # Python 3.x

from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)     # Python 2.x
    #print('hello {0}'.format(bar))   # Python 3.x
    return 'foo'

que = Queue.Queue()      # Python 2.x
#que = Queue()           # Python 3.x

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

It can be also easily adjusted to a multi-threaded environment:它也可以很容易地调整为多线程环境:

import Queue             # Python 2.x
#from queue import Queue # Python 3.x
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)     # Python 2.x
    #print('hello {0}'.format(bar))   # Python 3.x
    return 'foo'

que = Queue.Queue()      # Python 2.x
#que = Queue()           # Python 3.x

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         # Python 2.x
    #print(result)       # Python 3.x

Most answers I've found are long and require being familiar with other modules or advanced python features, and will be rather confusing to someone unless they're already familiar with everything the answer talks about.我发现的大多数答案都很长,需要熟悉其他模块或高级 python 功能,除非他们已经熟悉答案所涉及的所有内容,否则会让人感到困惑。

Working code for a simplified approach:简化方法的工作代码:

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)

Example code:示例代码:

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

Explanation: I wanted to simplify things significantly, so I created a ThreadWithResult class and had it inherit from threading.Thread .说明:我想显着简化事情,所以我创建了一个ThreadWithResult类并让它从threading.Thread继承。 The nested function function in __init__ calls the threaded function we want to save the value of, and saves the result of that nested function as the instance attribute self.result after the thread finishes executing. __init__中的嵌套函数function调用我们要保存其值的线程函数,并在线程执行完毕后将该嵌套函数的结果保存为实例属性self.result

Creating an instance of this is identical to creating an instance of threading.Thread .创建 this 的实例与创建threading.Thread的实例相同。 Pass in the function you want to run on a new thread to the target argument and any arguments that your function might need to the args argument and any keyword arguments to the kwargs argument.将要在新线程上运行的函数传递给target参数,并将函数可能需要的任何参数传递给args参数,并将任何关键字参数传递给kwargs参数。

eg例如

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

I think this is significantly easier to understand than the vast majority of answers, and this approach requires no extra imports!我认为这比绝大多数答案更容易理解,而且这种方法不需要额外的导入! I included the time and random module to simulate the behavior of a thread, but they're not required to achieve the functionality asked in the original question .我包含了timerandom模块来模拟线程的行为,但它们不是实现原始问题中提出的功能所必需的。

I know I'm answering this looong after the question was asked, but I hope this can help more people in the future!我知道在提出问题后我正在回答这个问题,但我希望这可以帮助更多的人!


EDIT : I created the save-thread-result PyPI package to allow you to access the same code above and reuse it across projects ( GitHub code is here ).编辑:我创建了save-thread-result PyPI 包,允许您访问上面的相同代码并跨项目重用它( GitHub 代码在这里)。 The PyPI package fully extends the threading.Thread class, so you can set any attributes you would set on threading.thread on the ThreadWithResult class as well! PyPI 包完全扩展了threading.Thread类,因此您也可以在ThreadWithResult类上设置您在threading.thread上设置的任何属性!

The original answer above goes over the main idea behind this subclass, but for more information, see the more detailed explanation (from the module docstring) here .上面的原始答案涵盖了该子类背后的主要思想,但有关更多信息,请参阅此处的更详细说明(来自模块文档字符串)

Quick usage example:快速使用示例:

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)

Parris / kindall's answer join / return answer ported to Python 3: Parris / kindall 的答案join / return答案移植到 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

Note, the Thread class is implemented differently in Python 3.请注意, Thread类在 Python 3 中的实现方式有所不同。

I stole kindall's answer and cleaned it up just a little bit.我偷了kindall的答案并稍微清理了一下。

The key part is adding *args and **kwargs to join() in order to handle the timeout关键部分是将 *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

UPDATED ANSWER BELOW更新的答案如下

This is my most popularly upvoted answer, so I decided to update with code that will run on both py2 and py3.这是我最受欢迎的答案,因此我决定使用可在 py2 和 py3 上运行的代码进行更新。

Additionally, I see many answers to this question that show a lack of comprehension regarding Thread.join().此外,我看到这个问题的许多答案表明对 Thread.join() 缺乏理解。 Some completely fail to handle the timeout arg.有些完全无法处理timeout参数。 But there is also a corner-case that you should be aware of regarding instances when you have (1) a target function that can return None and (2) you also pass the timeout arg to join().但是还有一个极端情况,当你有(1)一个可以返回None的目标函数和(2)你也将timeout参数传递给 join() 时,你应该注意关于实例的极端情况。 Please see "TEST 4" to understand this corner case.请参阅“测试 4”以了解这种极端情况。

ThreadWithReturn class that works with py2 and py3:与 py2 和 py3 一起使用的 ThreadWithReturn 类:

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

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

Some sample tests are shown below:一些示例测试如下所示:

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

Can you identify the corner-case that we may possibly encounter with TEST 4?您能确定我们在 TEST 4 中可能遇到的极端情况吗?

The problem is that we expect giveMe() to return None (see TEST 2), but we also expect join() to return None if it times out.问题是我们希望 giveMe() 返回 None(参见测试 2),但我们也希望 join() 在超时时返回 None。

returned is None means either: returned is None意味着:

(1) that's what giveMe() returned, or (1) 这就是 giveMe() 返回的,或者

(2) join() timed out (2) join() 超时

This example is trivial since we know that giveMe() will always return None.这个例子很简单,因为我们知道 giveMe() 总是返回 None。 But in real-world instance (where the target may legitimately return None or something else) we'd want to explicitly check for what happened.但在现实世界的实例中(目标可能合法地返回 None 或其他东西),我们希望明确检查发生了什么。

Below is how to address this corner-case:以下是解决这种极端情况的方法:

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

Using Queue :使用队列:

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

My solution to the problem is to wrap the function and thread in a class.我对这个问题的解决方案是将函数和线程包装在一个类中。 Does not require using pools,queues, or c type variable passing.不需要使用池、队列或 c 类型变量传递。 It is also non blocking.它也是非阻塞的。 You check status instead.您改为检查状态。 See example of how to use it at end of code.请参阅代码末尾的如何使用它的示例。

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

I'm using this wrapper, which comfortably turns any function for running in a Thread - taking care of its return value or exception.我正在使用这个包装器,它可以轻松地将任何函数转换为在Thread中运行 - 处理它的返回值或异常。 It doesn't add Queue overhead.它不会增加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

Usage Examples使用示例

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

Notes on threading module threading模块注意事项

Comfortable return value & exception handling of a threaded function is a frequent "Pythonic" need and should indeed already be offered by the threading module - possibly directly in the standard Thread class.线程函数的舒适返回值和异常处理是一种常见的“Pythonic”需求,并且确实应该由threading模块提供 - 可能直接在标准Thread类中。 ThreadPool has way too much overhead for simple tasks - 3 managing threads, lots of bureaucracy. ThreadPool对于简单的任务有太多的开销——3 个管理线程,很多官僚作风。 Unfortunately Thread 's layout was copied from Java originally - which you see eg from the still useless 1st (!) constructor parameter group .不幸的是, Thread的布局最初是从 Java 复制而来的——您可以从仍然无用的第一个 (!) 构造函数参数group中看到它。

Taking into consideration @iman comment on @JakeBiesinger answer I have recomposed it to have various number of threads:考虑到@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()

Based of what kindall mentioned, here's the more generic solution that works with Python3.根据所提到的内容,这是适用于 Python3 的更通用的解决方案。

import threading

class ThreadWithReturnValue(threading.Thread):
    def __init__(self, *init_args, **init_kwargs):
        threading.Thread.__init__(self, *init_args, **init_kwargs)
        self._return = None
    def run(self):
        self._return = self._target(*self._args, **self._kwargs)
    def join(self):
        threading.Thread.join(self)
        return self._return

Usage用法

        th = ThreadWithReturnValue(target=requests.get, args=('http://www.google.com',))
        th.start()
        response = th.join()
        response.status_code  # => 200

You can define a mutable above the scope of the threaded function, and add the result to that.您可以在线程函数的范围之上定义一个可变对象,并将结果添加到其中。 (I also modified the code to be python3 compatible) (我还修改了代码以兼容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)

This returns {'world!': 'foo'}这将返回{'world!': 'foo'}

If you use the function input as the key to your results dict, every unique input is guaranteed to give an entry in the results如果您使用函数输入作为结果字典的键,则保证每个唯一输入都会在结果中给出一个条目

join总是返回None ,我认为你应该继承Thread来处理返回码等等。

Define your target to定义你的目标
1) take an argument q 1) 取一个参数q
2) replace any statements return foo with q.put(foo); return 2) 用q.put(foo); return ) 替换任何return foo的语句; q.put(foo); return

so a function所以一个函数

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

would become会成为

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

and then you would proceed as such然后你会这样继续

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

And you can use function decorators/wrappers to make it so you can use your existing functions as target without modifying them, but follow this basic scheme.您可以使用函数装饰器/包装器来制作它,这样您就可以将现有函数用作target而不修改它们,但请遵循这个基本方案。

GuySoft's idea is great, but I think the object does not necessarily have to inherit from Thread and start() could be removed from interface: 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

As mentioned multiprocessing pool is much slower than basic threading.如前所述,多处理池比基本线程慢得多。 Using queues as proposeded in some answers here is a very effective alternative.使用此处某些答案中提出的队列是一种非常有效的选择。 I have use it with dictionaries in order to be able run a lot of small threads and recuperate multiple answers by combining them with dictionaries:我已经将它与字典一起使用,以便能够运行许多小线程并通过将它们与字典相结合来恢复多个答案:

#!/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)

Here is the version that I created of @Kindall's answer .这是我创建的@Kindall 答案的版本。

This version makes it so that all you have to do is input your command with arguments to create the new thread.这个版本使得您所要做的就是输入带有参数的命令来创建新线程。

This was made with Python 3.8:这是用 Python 3.8 制作的:

from threading import Thread
from typing import Any

def test(plug, plug2, plug3):
    print(f"hello {plug}")
    print(f'I am the second plug : {plug2}')
    print(plug3)
    return 'I am the return Value!'

def test2(msg):
    return f'I am from the second test: {msg}'

def test3():
    print('hello world')

def NewThread(com, Returning: bool, *arguments) -> Any:
    """
    Will create a new thread for a function/command.

    :param com: Command to be Executed
    :param arguments: Arguments to be sent to Command
    :param Returning: True/False Will this command need to return anything
    """
    class NewThreadWorker(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
    
    ntw = NewThreadWorker(target = com, args = (*arguments,))
    ntw.start()
    if Returning:
        return ntw.join()

if __name__ == "__main__":
    print(NewThread(test, True, 'hi', 'test', test2('hi')))
    NewThread(test3, True)

The shortest and simplest way I've found to do this is to take advantage of Python classes and their dynamic properties.我发现执行此操作的最短且最简单的方法是利用 Python 类及其动态属性。 You can retrieve the current thread from within the context of your spawned thread using threading.current_thread() , and assign the return value to a property.您可以使用threading.current_thread()从生成线程的上下文中检索当前线程,并将返回值分配给属性。

import threading

def some_target_function():
    # Your code here.
    threading.current_thread().return_value = "Some return value."

your_thread = threading.Thread(target=some_target_function)
your_thread.start()
your_thread.join()

return_value = your_thread.return_value
print(return_value)

One usual solution is to wrap your function foo with a decorator like一种常见的解决方案是用装饰器包装你的函数foo

result = queue.Queue()

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

Then the whole code may looks like that那么整个代码可能看起来像这样

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笔记

One important issue is that the return values may be unorderred .一个重要问题是返回值可能是无序的。 (In fact, the return value is not necessarily saved to the queue , since you can choose arbitrary thread-safe data structure ) (其实return value不一定保存到queue中,因为可以选择任意线程安全的数据结构)

Kindall's answer in Python3 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

This is a pretty old question, but I wanted to share a simple solution that has worked for me and helped my dev process.这是一个非常古老的问题,但我想分享一个对我有用并帮助我的开发过程的简单解决方案。

The methodology behind this answer is the fact that the "new" target function, inner is assigning the result of the original function (passed through the __init__ function) to the result instance attribute of the wrapper through something called closure.这个答案背后的方法是这样一个事实,即“新”目标函数inner正在通过称为闭包的东西将原始函数的结果(通过__init__函数传递)分配给包装器的result实例属性。

This allows the wrapper class to hold onto the return value for callers to access at anytime.这允许包装类保留调用者随时访问的返回值。

NOTE: This method doesn't need to use any mangled methods or private methods of the threading.Thread class, although yield functions have not been considered (OP did not mention yield functions).注意:此方法不需要使用threading.Thread类的任何损坏的方法或私有方法,尽管没有考虑 yield 函数(OP 没有提到 yield 函数)。

Enjoy!享受!

from threading import Thread as _Thread


class ThreadWrapper:
    def __init__(self, target, *args, **kwargs):
        self.result = None
        self._target = self._build_target_fn(target)
        self.thread = _Thread(
            target=self.target,
            *args,
            **kwargs
        )

    def _build_threaded_fn(self, func):
        def inner(*args, **kwargs):
            self.result = func(*args, **kwargs)
        return inner

Additionally, you can run pytest (assuming you have it installed) with the following code to demonstrate the results:此外,您可以使用以下代码运行pytest (假设您已安装)来演示结果:

import time
from commons import ThreadWrapper


def test():

    def target():
        time.sleep(1)
        return 'Hello'

    wrapper = ThreadWrapper(target=target)
    wrapper.thread.start()

    r = wrapper.result
    assert r is None

    time.sleep(2)

    r = wrapper.result
    assert r == 'Hello'

Best way... Define a global variable, then change the variable in the threaded function.最好的方法...定义一个全局变量,然后在线程函数中更改变量。 Nothing to pass in or retrieve back没有东西可以传入或取回

from threading import Thread

# global var
radom_global_var = 5

def function():
    global random_global_var
    random_global_var += 1

domath = Thread(target=function)
domath.start()
domath.join()
print(random_global_var)

# result: 6

You can use pool.apply_async() of ThreadPool() to return the value from test() as shown below:您可以使用ThreadPool()pool.apply_async()test()返回,如下所示:

from multiprocessing.pool import ThreadPool

def test(num1, num2):
    return num1 + num2

pool = ThreadPool(processes=1) # Here
result = pool.apply_async(test, (2, 3)) # Here
print(result.get()) # 5

And, you can also use submit() of concurrent.futures.ThreadPoolExecutor() to return the value from test() as shown below:并且,您还可以使用concurrent.futures.ThreadPoolExecutor()submit() ) 来返回test()的值,如下所示:

from concurrent.futures import ThreadPoolExecutor

def test(num1, num2):
    return num1 + num2

with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(test, 2, 3) # Here
print(future.result()) # 5

And, instead of return , you can use the array result as shown below:而且,您可以使用数组result代替return ,如下所示:

from threading import Thread

def test(num1, num2, r):
    r[0] = num1 + num2 # Instead of "return"

result = [None] # Here

thread = Thread(target=test, args=(2, 3, result))
thread.start()
thread.join()
print(result[0]) # 5

And instead of return , you can also use the queue result as shown below:而不是return ,您还可以使用队列result ,如下所示:

from threading import Thread
import queue

def test(num1, num2, q):
    q.put(num1 + num2) # Instead of "return" 

queue = queue.Queue() # Here

thread = Thread(target=test, args=(2, 3, queue))
thread.start()
thread.join()
print(queue.get()) # '5'

Why don't just use global variable? 为什么不只使用全局变量?

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)

Very simple way to get this done for such dummies like me: 为像我这样的假人完成此操作的非常简单的方法:

import queue
import threading

# creating queue instance
q = queue.Queue()

# creating threading class
class AnyThread():
    def __init__ (self):
        threading.Thread.__init__(self)

    def run(self):
        # in this class and function we will put our test target function
        test()

t = AnyThread()

# having our test target function
def test():
    # do something in this function:
    result = 3 + 2
    # and put result to a queue instance
    q.put(result)

for i in range(3): #calling our threading fucntion 3 times (just for example)
    t.run()
    output = q.get() # here we get output from queue instance
    print(output)

>>> 5
>>> 5
>>> 5

main thing here - is queue module. 这里的主要内容-是queue模块。 We create queue.Queue() instance and include it in our function. 我们创建queue.Queue()实例并将其包含在我们的函数中。 We feed it with our result which later we get beyond the thread. 我们将结果提供给它,后来我们超出了线程范围。

Please see one more example with arguments passed to our test function: 请再看一个将参数传递给我们的测试函数的示例:

import queue
import threading

# creating queue instance
q = queue.Queue()

# creating threading class
class AnyThread():
    def __init__ (self):
        threading.Thread.__init__(self)

    def run(self, a, b):
        # in this class and function we will put our execution test function
        test(a, b)

t = AnyThread()

# having our test target function
def test(a, b):
    # do something in this function:
    result = a + b
    # and put result to a queue instance
    q.put(result)

for i in range(3): #calling our threading fucntion 3 times (just for example)
    t.run(3+i, 2+i)
    output = q.get() # here we get output from queue instance
    print(output)

>>> 5
>>> 7
>>> 9

If only True or False is to be validated from a function's call, a simpler solution I find is updating a global list. 如果仅从函数调用中验证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 

This is more helpful where you want to find if any one of the threads had returned a false status to take the necessary action. 如果要查找是否有任何一个线程返回了错误的状态以采取必要的操作,这将更加有用。

I know this thread is old.... but I faced the same problem... If you are willing to use thread.join()我知道这个线程很旧......但我遇到了同样的问题......如果你愿意使用thread.join()

import threading

class test:

    def __init__(self):
        self.msg=""

    def hello(self,bar):
        print('hello {}'.format(bar))
        self.msg="foo"


    def main(self):
        thread = threading.Thread(target=self.hello, args=('world!',))
        thread.start()
        thread.join()
        print(self.msg)

g=test()
g.main()

I found using Queue worked.我发现使用 Queue 有效。 use the queue.put(result) in the threaded function, then queue.get() to retrieve results在线程函数中使用 queue.put(result),然后使用 queue.get() 来检索结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM