PyQt:如何向对象正在运行条件while循环的线程发送停止信号? [英] PyQt: How to send a stop signal into a thread where an object is running a conditioned while loop?

查看:139
本文介绍了PyQt:如何向对象正在运行条件while循环的线程发送停止信号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一些多线程处理.我有一个带有work方法的worker类,将其发送到单独的QThread中. work方法内部有条件的while循环.我希望能够向工作对象发送信号以将其停止(将_running条件更改为false).这将导致while循环退出,并从worker对象(连接到worker线程的退出插槽)发送完成信号.

I'm doing some multi-threading. I have a worker class with a work method, which I send into a separate QThread. The work method has a conditioned while loop inside. I want to be able to send a signal to the worker object to stop it (changing the _running condition to false). This will cause the while loop to exit, and a finished signal to be sent from the worker object (which is connected to the quit slot of the worker's thread).

错误条件是通过信号发送给工作对象的,但从未收到,这是因为while循环会阻塞其线程的事件循环.即使将QCoreApplication.processEvents()放入while循环中,也不会发生任何事情.问题出在哪儿?为什么不处理信号? (请注意,永远不会执行Worker的stop插槽中的print语句-但奇怪的是,线程似乎确实以错误的方式停止了.)

The false condition is sent to the worker object via a signal, but it is never received, which I believe is because the while loop blocks the event-loop of its thread. Even if I put QCoreApplication.processEvents() inside the while loop, nothing happens. Where is the problem? Why isn't the signal processed? (Notice that the print statement in the stop slot on the Worker is never executed - but the weird thing is, the thread does seem to stop in a wrong way).

这是代码:

import time, sys
from PyQt4.QtCore  import *
from PyQt4.QtGui import *

class Worker(QObject):
    sgnFinished = pyqtSignal()

    def __init__(self, parent):
        QObject.__init__(self, parent)

        self._running = True

    @pyqtSlot()
    def stop():
        print 'stop signal received, switching while loop condition to false'
        self._running = False

    @pyqtSlot()    
    def work(self):
        while self._running:                 #this blocks the thread, if changed to an if clause, thread finishes as expected!
            QCoreApplication.processEvents() #this doesn't help!
            time.sleep(0.1)
            print 'doing work...'

        #do some cleanup here, then signal the worker is done
        self.sgnFinished.emit()


class Client(QObject):
    sgnStop = pyqtSignal()

    def __init__(self, parent):
        QObject.__init__(self, parent)

        self._thread = None
        self._worker = None

    def toggle(self, enable):
        if enable:
            if not self._thread:
                self._thread = QThread()

            self._worker = Worker(None)
            self._worker.moveToThread(self._thread)

            self._worker.sgnFinished.connect(self.on_worker_done)
            self.sgnStop.connect(self._worker.stop)

            self._thread.started.connect(self._worker.work)
            self._thread.start()
        else:
            print 'sending stop signal to the worker object'
            self.sgnStop.emit() #send a queuedconnection type signal to the worker, because its in another thread

    @pyqtSlot() 
    def on_worker_done(self):
        print 'workers job was interrupted manually'
        self._thread.quit()
        #self._thread.wait() not sure this is neccessary

if __name__ == '__main__':
    app = QCoreApplication(sys.argv)

    client = Client(None)
    client.toggle(True)
    raw_input('Press something')
    client.toggle(False)

推荐答案

您的示例中有两个主要问题:

There are two main problems in your example:

首先,您正在发出停止工作者的信号,但是由于该信号是跨线程的,因此它将被发布在接收者的事件队列中.但是,工作程序正在运行阻塞的while循环,因此无法处理未决事件.有几种方法可以解决此问题,但最简单的方法可能是直接调用工作程序的stop方法而不是使用信号.

Firstly, you are emitting a signal to stop the worker, but since the signal is cross-thread, it will be posted in the receiver's event-queue. However, the worker is running a blocking while-loop, so pending events cannot be processed. There are a few ways to work around this, but probably the simplest is to simply call the worker's stop method directly instead of using a signal.

第二,您没有在主线程中显式运行事件循环,因此无法将从worker发送的跨线程信号排队.不过,更重要的是,在用户按下某个键之后,也没有任何事情可以阻止程序退出-因此,客户端和工作人员将立即被垃圾回收.

Secondly, you are not explicitly running an event-loop in the main thread, so cross-thread signals sent from the worker cannot be queued. More importantly, though, there is also nothing to stop the program exiting after the user presses a key - so the client and worker will be immediately garbage-collected.

以下是示例的重写版本,可解决所有问题:

Below is a re-written version of your example which fixes all the issues:

import time, sys
from PyQt4.QtCore  import *
from PyQt4.QtGui import *

class Worker(QObject):
    sgnFinished = pyqtSignal()

    def __init__(self, parent):
        QObject.__init__(self, parent)
        self._mutex = QMutex()
        self._running = True

    @pyqtSlot()
    def stop(self):
        print 'switching while loop condition to false'
        self._mutex.lock()
        self._running = False
        self._mutex.unlock()

    def running(self):
        try:
            self._mutex.lock()
            return self._running
        finally:
            self._mutex.unlock()

    @pyqtSlot()
    def work(self):
        while self.running():
            time.sleep(0.1)
            print 'doing work...'
        self.sgnFinished.emit()

class Client(QObject):
    def __init__(self, parent):
        QObject.__init__(self, parent)
        self._thread = None
        self._worker = None

    def toggle(self, enable):
        if enable:
            if not self._thread:
                self._thread = QThread()

            self._worker = Worker(None)
            self._worker.moveToThread(self._thread)
            self._worker.sgnFinished.connect(self.on_worker_done)

            self._thread.started.connect(self._worker.work)
            self._thread.start()
        else:
            print 'stopping the worker object'
            self._worker.stop()

    @pyqtSlot()
    def on_worker_done(self):
        print 'workers job was interrupted manually'
        self._thread.quit()
        self._thread.wait()
        if raw_input('\nquit application [Yn]? ') != 'n':
            qApp.quit()

if __name__ == '__main__':

    # prevent some harmless Qt warnings
    pyqtRemoveInputHook()

    app = QCoreApplication(sys.argv)

    client = Client(None)

    def start():
        client.toggle(True)
        raw_input('Press something\n')
        client.toggle(False)

    QTimer.singleShot(10, start)

    sys.exit(app.exec_())

这篇关于PyQt:如何向对象正在运行条件while循环的线程发送停止信号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆