本文介绍了如何从QThread和Queue运行的函数中返回值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请说明我们如何从Queue管理的线程中发送/接收数据.

Please explain how do we send/receive data from Thread managed by Queue....

第一个子类'QThread'定义了它的run()方法,该方法在调用QThread.start()时启动:

First I subclass 'QThread' defining its run() method which is started when QThread's.start() is called:

class SimpleThread(QtCore.QThread):
    def __init__(self, queue, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue=queue        
    def run(self):
        while True:
            arg=self.queue.get() 
            self.fun(arg)    
            self.queue.task_done()
    def fun(self, arg):
        for i in range (3):
            print 'fun: %s'%i
            self.sleep(1)
        return arg+1

然后我声明两个线程实例(因此仅占用两个CPU内核),将self.queue实例作为参数发送.

Then I declare two Thread instances (so only two CPU cores are taken) sending self.queue instance as an argument.

self.queue=queue.Queue()
for i in range(2):
    thread=SimpleThread(self.queue)
    thread.start()

现在,如果我理解正确的话,thread.start()没有启动任何操作.真正的开始"仅在我调用queue.put()时发生:

Now if I understand it correctly thread.start() is not starting anything. The real "start" happens only when I call queue.put():

for arg in [1,2,3]: self.queue.put(arg)

最后一行是进行真实"调用的原因.除了创建和启动队列项目 put()之外,还可以将任意值保存到每个队列项目. .put()一次执行几项操作:它创建,启动,将处理移动通过队列,并允许在队列项的内部"放置一个变量(以后可以从函数处理器内部进行检索:使用队列项目的'.get()`方法).

This last line is what makes a "real" call. Aside from creating and starting Queue item put() allows to save any arbitrary value to each Queue item. .put() does several things at once: it creates, it starts, it moves the processing through the Queue and it allows to place a variable "inside" of the queue item (which later can be retrieved from inside of the function-processor: using Queue item's '.get()` method).

但是如何从fun()函数返回值. 常规" fun()return resultValue不起作用.而且我不能使用self.queue.put()方法,因为该方法除了存储数据之外,还会创建"新的队列项目...

But how do I return the value from fun() function. A "regular" fun()'s return resultValue doesn't work. And I can't use self.queue.put() method since this method aside from storing a data "creates" a new queue item...

这里是经过微调的代码(从另一篇文章中复制/粘贴),显示了有关如何从已完成的线程中返回值的方法.我不确定这里使用的方法是否适用于QThread ...如果我错了,请纠正我:

Here is slightly tweaked code (copy/pasted from another post) showing an approach on how to return a value from completed Thread. I am not sure if the the approach used here would work with QThread... please correct me if I am wrong:

import os, sys
import threading
import Queue

def callMe(incomingFun, daemon=False):
    def execute(_queue, *args, **kwargs):
        result=incomingFun(*args, **kwargs)
        _queue.put(result)

    def wrap(*args, **kwargs):
        _queue=Queue.Queue()
        _thread=threading.Thread(target=execute, args=(_queue,)+args, kwargs=kwargs)
        _thread.daemon=daemon
        _thread.start()
        _thread.result_queue=_queue        
        return _thread

    return wrap

@callMe
def localFunc(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

thread=localFunc(10)

# this blocks, waiting for the result
result = thread.result_queue.get()
print result

推荐答案

在正常情况下,您将使用结果队列将结果发送回去,然后运行其他一些线程等待结果:

In normal circumstances you'd use a result queue to send results back, and then have some other thread running that waits for the results:

class SimpleThread(QtCore.QThread):
    def __init__(self, queue, result_queue, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue=queue
        self.result_queue = result_queue

    def run(self):
        while True:
            arg=self.queue.get() 
            self.fun(arg)    
            self.queue.task_done()

    def fun(self, arg):
        for i in range (3):
            print 'fun: %s'%i
            self.sleep(1)
        self.result_queue.put(arg+1)

def handle_results(result_queue):
   while True:
       result = result_queue.get()
       print("Got result {}".format(result))

主线程:

self.queue=queue.Queue()
self.result_queue = queue.Queue()

result_handler = threading.Thread(target=handle_results, self.result_queue)
for i in range(2):
    thread=SimpleThread(self.queue, self.result_queue)
    thread.start()

以这种方式进行操作将使您在等待结果时不会阻塞GUI的事件循环.这就是multiprocessing.pool.ThreadPool的等效形式:

Doing it this way will keep you from blocking the GUI's event loop while you wait for the results. Here's what the equivalent would look like with multiprocessing.pool.ThreadPool:

from multiprocessing.pool import ThreadPool
import time


def fun(arg):
    for i in range (3):
        print 'fun: %s'%i
        time.sleep(1)
    return arg+1

def handle_result(result):
   print("got result {}".format(result))

pool = ThreadPool(2)
pool.map_async(fun, [1,2,3], callback=handle_result)

这要简单得多.它在内部创建一个结果处理线程,当fun完成时,它将自动为您调用handle_result.

Which is a lot simpler. It internally creates a result handling thread, which will automatically call handle_result for you when fun completes.

也就是说,您正在使用QThread,并且您希望结果更新GUI小部件,因此您确实希望将结果发送回主线程,而不是结果处理线程.在这种情况下,使用Qt的信号系统是有意义的,这样您在收到结果时就可以安全地更新GUI:

That said, you're using QThread, and you want the results to update GUI widgets, so you really want your results to be sent back to the main thread, not to a result handling thread. In that case, it makes sense to use Qt's signaling system, so that you can safely update the GUI when you receive the result:

from PyQt4 import QtCore, QtGui
import sys
import Queue as queue

class ResultObj(QtCore.QObject):
    def __init__(self, val):
        self.val = val

class SimpleThread(QtCore.QThread):
    finished = QtCore.pyqtSignal(object)

    def __init__(self, queue, callback, parent=None):
        QtCore.QThread.__init__(self, parent)      
        self.queue = queue
        self.finished.connect(callback)

    def run(self):
        while True:
            arg = self.queue.get() 
            if arg is None: # None means exit
                print("Shutting down")
                return
            self.fun(arg)    

    def fun(self, arg):
        for i in range(3):
            print 'fun: %s' % i
            self.sleep(1)
        self.finished.emit(ResultObj(arg+1))


class AppWindow(QtGui.QMainWindow):
    def __init__(self):
        super(AppWindow, self).__init__()
        mainWidget = QtGui.QWidget()
        self.setCentralWidget(mainWidget)
        mainLayout = QtGui.QVBoxLayout()
        mainWidget.setLayout(mainLayout)  
        button = QtGui.QPushButton('Process')
        button.clicked.connect(self.process)
        mainLayout.addWidget(button)

    def handle_result(self, result):
        val = result.val
        print("got val {}".format(val))
        # You can update the UI from here.

    def process(self):
        MAX_CORES=2
        self.queue = queue.Queue()
        self.threads = []
        for i in range(MAX_CORES):
            thread = SimpleThread(self.queue, self.handle_result)
            self.threads.append(thread)
            thread.start()  

        for arg in [1,2,3]:
            self.queue.put(arg)

        for _ in range(MAX_CORES): # Tell the workers to shut down
            self.queue.put(None)

app = QtGui.QApplication([])
window = AppWindow()
window.show()
sys.exit(app.exec_())

按下按钮时输出:

fun: 0
 fun: 0
fun: 1
 fun: 1
fun: 2
 fun: 2
fun: 0
got val 2
got val 3
Shutting down
fun: 1
fun: 2
Shutting down
got val 4

这篇关于如何从QThread和Queue运行的函数中返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 05:59