打断 QThread 的休眠

3

我希望知道如何暂停一个QThread并在接收到信号后恢复。我已经阅读并了解我可以执行如下操作:

def run(self):
    ...
    self.ready=False
    while not self.ready:
        self.sleep(1)
    ...
...
@QtCore.Slot()
def set_ready(self):
    self.ready = True

然而,我想做的是避免在线程中轮询。我不想将睡眠时间设置为短暂的时间并不断检查。我希望在主线程发出信号后进入睡眠状态,直到收到继续执行的信号。

我在线程中正在做的事情如下:

(pseudo code)
with open file:
    read a block of data
    while data:
        sendThread = send the block via UDP
        read the next block of data
        while not ready or sendThread.isRunning:
            sleep(0.1)

在我的主线程中,我设置了一个 QtNetwork.QUdpSocket 来连接 readyRead 方法来处理传入的数据包并解码它们。当它获取到我等待的响应时,它会发送一个信号给 set_ready 槽来告诉线程发送另一个数据包。但我不总是知道对方系统响应需要多长时间,虽然我可能有一些较长的超时值,例如 30 秒左右。
是否有一种方法可以中断线程的睡眠?这样我就可以像这样做:
sleep(30)
if not ready:
    Timeout occurred stop processing
else:
    Continue processing.
1个回答

5
您可以使用使用QThreadsworker模式轻松完成此操作。在QThread文档中有一个示例。具体代码会略有不同,这取决于您是使用PyQt还是PySide(从您的示例看来,似乎您正在使用PySide)。

PySidePyQt相比存在一个值得注意的问题,即他们没有包装QtCore.Q_ARG,因此在PyQt中,你通常可以使用QMetaObject.invokeMethod在主线程上调用Worker对象上的槽(带参数),但是在PySide中无法直接这样做,必须创建一个虚拟信号(即Main.send_signal)连接到工作器上的槽,以便你可以从主线程调用它。

import time
import sys
from PySide import QtCore, QtGui


class Worker(QtCore.QObject):

    send_signal = QtCore.Signal(str) # using PySide
    # QtCore.pyqtSignal(str) ## using PyQt

    # @QtCore.pyqtSlot(str)
    @QtCore.Slot(str)
    def receive_slot(self, data):
        # data could be a filepath
        # open file
        # ... do stuff
        # close file
        QtCore.QThread.sleep(1) # to simulate doing stuff
        self.send_signal.emit(data + ' success')


class Main(QtGui.QWidget):

    send_signal = QtCore.Signal(str)

    def __init__(self):
        super(Main, self).__init__()
        self.worker = Worker()
        self.thread = QtCore.QThread(self)
        self.worker.moveToThread(self.thread)
        self.worker.send_signal.connect(self.receive_slot)
        self.send_signal.connect(self.worker.receive_slot)
        self.thread.start()
        self.send_signal.emit('Start')

    @QtCore.Slot(str)
    def receive_slot(self, data):
        print 'Main: {}'.format(data)
        self.send_signal.emit('Message {}'.format(time.time()))


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window = Main()
    window.show()
    app.exec_()

1
@busfault 更新了答案以展示我的意思。如果你想让用户能够取消操作,你可以在 worker 上创建一个名为 cancel 的实例变量,并将其设置为 False,然后在 worker 函数中定期检查它。如果主线程想要取消 worker 线程,只需设置该变量 self.worker.cancel = True。当 worker 检查到变量为 True 时,应该停止执行。 - Brendan Abel
如果您在线程中打开文件,可以分块继续阅读,并在数据可用时将其发送回主线程。 - Brendan Abel
Brendan,谢谢,不过我已经在我的继承线程类中实现了取消部分。 - Tom Myddeltyn
我已经在线程中打开了文件,主线程根本不需要从文件中获取数据。我只需要读取块并发送数据包,然后需要等待特定的数据包指示作业完成,然后可以发送另一个数据包。还有其他与操作无关的数据包也需要处理。 - Tom Myddeltyn
1
你可以继续使用继承的 Thread 方法,而不是 worker model,但要意识到 QThread 实际上存在于主线程中,只有 run 函数在单独的线程中执行,因此在线程上创建的任何信号或槽实际上在主线程中运行。如果你需要在主线程和工作线程之间发送数据,通常最好使用 worker model。 - Brendan Abel
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接