Python 如何从QThread和Queue运行的函数返回值
请解释我们如何从队列管理的线程发送/接收数据 第一个子类“QThread”定义了它的Python 如何从QThread和Queue运行的函数返回值,python,multithreading,qt,pyqt,Python,Multithreading,Qt,Pyqt,请解释我们如何从队列管理的线程发送/接收数据 第一个子类“QThread”定义了它的run()方法,该方法在调用QThread的.start()时启动: class SimpleThread(QtCore.QThread): def __init__(self, queue, parent=None): QtCore.QThread.__init__(self, parent) self.queue=queue def
run()
方法,该方法在调用QThread
的.start()
时启动:
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
实例
self.queue=queue.Queue()
for i in range(2):
thread=SimpleThread(self.queue)
thread.start()
现在,如果我理解正确的话,thread.start()
不会启动任何东西。真正的“开始”只有在我调用queue.put()时才会发生:
这最后一行才是真正的电话。除了创建和启动队列项目之外,put()
允许将任意值保存到每个队列项目.put()
一次执行多项操作:它创建、启动、在队列中移动处理,并允许在队列项的“内部”放置变量(稍后可以从函数处理器内部检索该变量:使用队列项的“.get()`方法)
但是如何从fun()
函数返回值呢。“常规”的fun()
返回结果值不起作用。我不能使用self.queue.put()方法,因为这个方法除了存储数据外,还“创建”了一个新的队列项
稍后编辑:
下面是稍微调整的代码(从另一篇文章复制/粘贴),展示了如何从已完成的线程返回值的方法。我不确定这里使用的方法是否适用于QThread。。。如果我错了,请纠正我:
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
在正常情况下,您会使用结果队列将结果发送回,然后让其他线程运行以等待结果:
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的事件循环。以下是使用多处理.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\u result
也就是说,您使用的是QThread
,您希望结果更新GUI小部件,因此您确实希望将结果发送回主线程,而不是结果处理线程。在这种情况下,使用Qt的信号系统是有意义的,这样您可以在收到结果时安全地更新GUI:
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派生。您可以使用信号和插槽将消息从工作线程传递到主线程。但是您的任务要方便得多。@PavelStrakhov我不认为在任何Qt的Python绑定中都可以使用QtConcurrent
。首先,让我们澄清一下如何处理fun()
函数的返回值。然后我们可以讨论应该和不应该派生什么。您希望SimpleThread
永远在后台运行,还是在您向它传递了任意数量的值之后它应该结束?通常,您会为结果创建第二个返回队列。很好的示例代码!再次感谢。这是我在很长一段时间里学到的最好的资源之一!谢谢你的教育!在您的上一个示例中,有一个拼写错误s/finshed/finished,我需要这个答案pyqt5@lone_coder看起来pyqt5中仍然存在pyqtSignal
,因此修改这个答案应该很简单。@dano。是的,我已经弄明白了,谢谢你!
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