how to get the return value from a thread in python?

来源:互联网 发布:淘宝小商品推广免单 编辑:程序博客网 时间:2024/06/05 22:41

how to get the return value from a thread in python?

up vote19down votefavorite
6

How can I access that return value 'foo' from the thread?

def foo(bar):  print 'hello {0}'.format(bar)  return 'foo'from threading import Threadt = Thread(target=foo, args=('world!',))t.start()x = t.join()print x

The one obvious way to do it, above, seems to just return None in x.

share|improve this question
 add comment

5 Answers

activeoldestvotes
up vote11down voteaccepted

FWIW, the multiprocessing module has a nice interface for this using the Pool class. And if you want to stick with threads rather than processes, you can just use themultiprocessing.pool.ThreadPool class as a drop-in replacement.

def foo(bar, baz):  print 'hello {0}'.format(bar)  return 'foo' + bazfrom multiprocessing.pool import ThreadPoolpool = ThreadPool(processes=1)async_result = pool.apply_async(foo, ('world', 'foo')) # tuple of args for foo# do some other stuff in the main processreturn_val = async_result.get()  # get the return value from your function.
share|improve this answer
 
 
this is the best answer as it's non-intrusive –  Jayen yesterday
add comment
up vote22down vote

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 Threadthreads = [None] * 10results = [None] * 10for i in range(len(threads)):    threads[i] = Thread(target=foo, args=('world!', results, i))    threads[i].start()# do some other stufffor 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 aThread subclass like the following:

from threading import Threaddef 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._returntwrv = 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.

share|improve this answer
 
 
thanks, i can see that that would be fine as a workaround, but it changes the function definition so that it doesn't really return anything. i wanted to know in my original case, where does that 'foo' actually go...? – wim Aug 2 '11 at 3:53 
 
@wim: Return values go somewhere only if you put them somewhere. Unfortunately, in the case of Thread, all that happens inside the class -- the default run() method does not store off the return value, so you lose it. You could write your own Thread subclass to handle this, though. I've taken a whack at it in my message. –  kindall Aug 3 '11 at 1:20
2 
cool, thanks for the example! i wonder why Thread was not implemented with handling a return value in the first place, it seems like an obvious enough thing to support. –  wim Aug 3 '11 at 1:28
add comment
up vote7down vote

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-inQueue.Queue class, as it offers thread safety.

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:

@threadeddef long_task(x):    import time    x = x + 5    time.sleep(5)    return x# does not block, returns Thread objecty = long_task(10)print y# this blocks, waiting for the resultresult = 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.

share|improve this answer
 
 
I can't seem to get this to work; I get an error stating AttributeError: 'module' object has no attribute 'Lock' this appears to be emanating from the line y = long_task(10)... thoughts? – sadmicrowave Sep 13 at 2:26
 
The code doesn't explicitly use Lock, so the problem could be somewhere else in your code. You may want to post a new SO question about it –  bj0 Sep 14 at 4:18
add comment
up vote2down vote

join always return None, i think you should subclass Thread to handle return codes and so.

share|improve this answer
 add comment
up vote2down vote

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. It is also non blocking. You check status instead. See example of how to use it at end of code.

import threadingclass 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.datadef add(x,y):    return x +yadd_worker = ThreadWorker(add)print add_worker.start((1,2,))print add_worker.status()print add_worker.get_results()
share|improve this answer
 add comment
原创粉丝点击