简体   繁体   English

从线程返回值

[英]Return value from thread

如何让线程将元组或我选择的任何值返回给 Python 中的父级?

I suggest you instantiate a Queue.Queue before starting the thread, and pass it as one of the thread's args: before the thread finishes, it .put s the result on the queue it received as an argument.我建议你在启动线程之前实例化一个Queue.Queue ,并将它作为线程的参数之一传递:在线程完成之前,它.put将结果放在它作为参数接收的队列中。 The parent can .get or .get_nowait it at will.父母可以随意.get.get_nowait

Queues are generally the best way to arrange thread synchronization and communication in Python: they're intrinsically thread-safe, message-passing vehicles -- the best way to organize multitasking in general!-)队列通常是在 Python 中安排线程同步和通信的最佳方式:它们本质上是线程安全的消息传递工具——通常是组织多任务处理的最佳方式!-)

You should pass a Queue instance as a parameter then you should .put() your return object into the queue.您应该将 Queue 实例作为参数传递,然后您应该 .put() 您的返回对象到队列中。 You can gather the return value via queue.get() whatever object you put.您可以通过 queue.get() 收集您放置的任何对象的返回值。

Sample:样本:

queue = Queue.Queue()
thread_ = threading.Thread(
                target=target_method,
                name="Thread1",
                args=[params, queue],
                )
thread_.start()
thread_.join()
queue.get()

def target_method(self, params, queue):
 """
 Some operations right here
 """
 your_return = "Whatever your object is"
 queue.put(your_return)

Use for multiple threads:用于多线程:

#Start all threads in thread pool
    for thread in pool:
        thread.start()
        response = queue.get()
        thread_results.append(response)

#Kill all threads
    for thread in pool:
        thread.join()

I use this implementation and it works great for me.我使用这个实现,它对我很有用。 I wish you do so.我希望你这样做。

Use lambda to wrap your target thread function and pass its return value back to the parent thread using a queue .使用lambda来包装目标线程函数并使用queue将其返回值传递回父线程。 (Your original target function remains unchanged without extra queue parameter.) (在没有额外队列参数的情况下,您的原始目标函数保持不变。)

Sample code:示例代码:

import threading
import queue
def dosomething(param):
    return param * 2
que = queue.Queue()
thr = threading.Thread(target = lambda q, arg : q.put(dosomething(arg)), args = (que, 2))
thr.start()
thr.join()
while not que.empty():
    print(que.get())

Output:输出:

4

If you were calling join() to wait for the thread to complete, you could simply attach the result to the Thread instance itself and then retrieve it from the main thread after the join() returns.如果您调用 join() 等待线程完成,您可以简单地将结果附加到 Thread 实例本身,然后在 join() 返回后从主线程中检索它。

On the other hand, you don't tell us how you intend to discover that the thread is done and that the result is available.另一方面,您没有告诉我们您打算如何发现线程已完成以及结果可用。 If you already have a way of doing that, it will probably point you (and us, if you were to tell us) to the best way of getting the results out.如果您已经有办法做到这一点,它可能会为您(以及我们,如果您告诉我们)指出获得结果的最佳方式。

I'm surprised nobody mentioned that you could just pass it a mutable:我很惊讶没有人提到你可以将它传递给一个可变的:

>>> thread_return={'success': False}
>>> from threading import Thread
>>> def task(thread_return):
...  thread_return['success'] = True
... 
>>> Thread(target=task, args=(thread_return,)).start()
>>> thread_return
{'success': True}

perhaps this has major issues of which I'm unaware.也许这有我不知道的重大问题。

Another approach is to pass a callback function to the thread.另一种方法是将回调函数传递给线程。 This gives a simple, safe and flexible way to return a value to the parent, anytime from the new thread.这提供了一种简单、安全和灵活的方法,可以随时从新线程向父级返回值。

# A sample implementation

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, cb):
        threading.Thread.__init__(self)
        self.callback = cb

    def run(self):
        for i in range(10):
            self.callback(i)
            time.sleep(1)


# test

import sys

def count(x):
    print x
    sys.stdout.flush()

t = MyThread(count)
t.start()

You can use synchronised queue module.您可以使用同步队列模块。
Consider you need to check a user infos from database with a known id:考虑您需要使用已知 ID 从数据库中检查用户信息:

def check_infos(user_id, queue):
    result = send_data(user_id)
    queue.put(result)

Now you can get your data like this:现在您可以像这样获取数据:

import queue, threading
queued_request = queue.Queue()
check_infos_thread = threading.Thread(target=check_infos, args=(user_id, queued_request))
check_infos_thread.start()
final_result = queued_request.get()

POC:概念验证:

import random
import threading

class myThread( threading.Thread ):
    def __init__( self, arr ):
        threading.Thread.__init__( self )
        self.arr = arr
        self.ret = None

    def run( self ):
        self.myJob( self.arr )

    def join( self ):
        threading.Thread.join( self )
        return self.ret

    def myJob( self, arr ):
        self.ret = sorted( self.arr )
        return

#Call the main method if run from the command line.
if __name__ == '__main__':
    N = 100

    arr = [ random.randint( 0, 100 ) for x in range( N ) ]
    th = myThread( arr )
    th.start( )
    sortedArr = th.join( )

    print "arr2: ", sortedArr

For easy programs the above answeres look a little bit like overkill to me.对于简单的程序,上述答案对我来说有点矫枉过正。 I would en-nicen the mutable approach:我会使用可变方法:

class RetVal:
 def __init__(self):
   self.result = None


def threadfunc(retVal):
  retVal.result = "your return value"

retVal = RetVal()
thread = Thread(target = threadfunc, args = (retVal))

thread.start()
thread.join()
print(retVal.result)

Well, in the Python threading module, there are condition objects that are associated to locks.好吧,在 Python 线程模块中,有与锁相关联的条件对象。 One method acquire() will return whatever value is returned from the underlying method.一种方法acquire()将返回从底层方法返回的任何值。 For more information:Python Condition Objects有关更多信息:Python 条件对象

Based on jcomeau_ictx's suggestion.基于 jcomeau_ictx 的建议。 The simplest one I came across.我遇到的最简单的一种。 Requirement here was to get exit status staus from three different processes running on the server and trigger another script if all three are successful.这里的要求是从服务器上运行的三个不同进程中获取退出状态,如果三个都成功则触发另一个脚本。 This seems to be working fine这似乎工作正常

  class myThread(threading.Thread):
        def __init__(self,threadID,pipePath,resDict):
            threading.Thread.__init__(self)
            self.threadID=threadID
            self.pipePath=pipePath
            self.resDict=resDict

        def run(self):
            print "Starting thread %s " % (self.threadID)
            if not os.path.exists(self.pipePath):
            os.mkfifo(self.pipePath)
            pipe_fd = os.open(self.pipePath, os.O_RDWR | os.O_NONBLOCK )
           with os.fdopen(pipe_fd) as pipe:
                while True:
                  try:
                     message =  pipe.read()
                     if message:
                        print "Received: '%s'" % message
                        self.resDict['success']=message
                        break
                     except:
                        pass

    tResSer={'success':'0'}
    tResWeb={'success':'0'}
    tResUisvc={'success':'0'}


    threads = []

    pipePathSer='/tmp/path1'
    pipePathWeb='/tmp/path2'
    pipePathUisvc='/tmp/path3'

    th1=myThread(1,pipePathSer,tResSer)
    th2=myThread(2,pipePathWeb,tResWeb)
    th3=myThread(3,pipePathUisvc,tResUisvc)

    th1.start()
    th2.start()
    th3.start()

    threads.append(th1)
    threads.append(th2)
    threads.append(th3)

    for t in threads:
        print t.join()

    print "Res: tResSer %s tResWeb %s tResUisvc %s" % (tResSer,tResWeb,tResUisvc)
    # The above statement prints updated values which can then be further processed

The following wrapper function will wrap an existing function and return an object which points both to the thread (so that you can call start() , join() , etc. on it) as well as access/view its eventual return value.以下包装器函数将包装一个现有函数并返回一个对象,该对象既指向线程(以便您可以在其上调用start()join()等)以及访问/查看其最终返回值。

def threadwrap(func,args,kwargs):
   class res(object): result=None
   def inner(*args,**kwargs): 
     res.result=func(*args,**kwargs)
   import threading
   t = threading.Thread(target=inner,args=args,kwargs=kwargs)
   res.thread=t
   return res

def myFun(v,debug=False):
  import time
  if debug: print "Debug mode ON"
  time.sleep(5)
  return v*2

x=threadwrap(myFun,[11],{"debug":True})
x.thread.start()
x.thread.join()
print x.result

It looks OK, and the threading.Thread class seems to be easily extended(*) with this kind of functionality, so I'm wondering why it isn't already there.看起来不错,而且threading.Thread类似乎很容易使用这种功能扩展(*),所以我想知道为什么它还没有。 Is there a flaw with the above method?上述方法有缺陷吗?

(*) Note that husanu's answer for this question does exactly this, subclassing threading.Thread resulting in a version where join() gives the return value. (*) 请注意,husanu 对这个问题的回答正是这样做的,子类threading.Thread导致join()给出返回值的版本。

Here is a code which implements multi-threading.这是一个实现多线程的代码。

Thread 1 is adding numbers from 10 to 20. Thread 2 is adding numbers from 21 to 30.线程 1 将数字从 10 添加到 20。线程 2 将添加从 21 到 30 的数字。

Finally the output is returned to the main program where it can perform final addition.最后,输出返回到主程序,在那里它可以执行最后的加法。 (not shown in this program) but you can use a numpy call. (此程序中未显示)但您可以使用 numpy 调用。

import threading
import os
import queue

def task1(num, queue): 
    print("\n Current thread: {}".format(threading.current_thread().name)) 
    count = 0
    sum1 = 0
    while count <= 10:
        sum1 = sum1 + num
        num = num + 1
        count = count + 1
    print('\n'+str(sum1))
    queue.put(sum1)


if __name__ == "__main__":

    queue = queue.Queue()

    # print ID of current process 
    print("\n Process ID is: {}".format(os.getpid())) 

    # print name of main thread 
    print("\n Main thread is: {}".format(threading.main_thread().name)) 

    # creating threads 
    t1 = threading.Thread(target=task1, name='t1',args=[10,queue]) 
    t2 = threading.Thread(target=task1, name='t2',args=[21,queue])

    #Store thread names in a list
    pool = [t1,t2]

    #Used to store temporary values
    thread_results = []

    # starting threads
    #Start all threads in thread pool
    for thread in pool:
        thread.start()
        response = queue.get()
        thread_results.append(response)

    #Kill all threads
    for thread in pool:
        thread.join()

    print(thread_results)

I came up with a simple solution to get the result of a thread using a closure function within the threading.Thread subclass.我想出了一个简单的解决方案来使用threading.Thread子类中的闭包函数来获取线程的结果。 I also created the save-thread-result PyPI package shortly after to allow access to the same code below to be reused across projects ( GitHub code is here ), and since the subclass fully extends the threading.Thread class, you can set any attributes you would set on threading.thread on the ThreadWithResult class as well:我还在不久之后创建了save-thread-result PyPI 包,以允许访问下面相同的代码以在项目之间重用( GitHub 代码在这里),并且由于子类完全扩展了threading.Thread类,因此您可以设置任何属性您还可以在ThreadWithResult类上设置threading.thread

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)

Quick usage example:快速使用示例:

import threading, time, random

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)

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 created a ThreadWithResult class and had it inherit from threading.Thread to simplify things significantly.说明:我创建了一个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__的嵌套函数函数调用我们要保存其值的线程函数,并在线程执行完毕后将该嵌套函数的结果保存为实例属性 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 in the actual implementation.我包含了timerandom模块来模拟线程的行为,但在实际实现中不需要它们。

For more information, see the more detailed explanation (from the module docstring) here .有关更多信息,请参阅此处的更详细说明(来自模块文档字符串)。

Downloading the module from PyPI and using it:从 PyPI下载模块并使用它:

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 hasattr(thread, 'result'):
    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
# in ThreadWithResult:
help(ThreadWithResult)

First posted here首先在这里发布

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

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