简体   繁体   English

使用 QThreadPool 时如何杀死工作线程?

[英]How to kill a worker thread when using QThreadPool?

I have this server class subclassed QRunnable which first broadcast UDP then switch to TCP when a client has connected.我有这台服务器 class 子类 QRunnable ,它首先广播 UDP 然后在客户端连接时切换到 TCP 。 The server class has an event loop to kill itself and a queue to pop then sending to the client.服务器 class 有一个事件循环来杀死自己和一个队列来弹出然后发送到客户端。

Then I have a simple GUI which I wanted to input text then send it out through the server when I press the button.然后我有一个简单的 GUI,我想输入文本,然后在按下按钮时通过服务器将其发送出去。

But when I close the GUI my thread class won't stop which I called via QThreadpool, is there a way to properly stop the worker thread after I close the GUI?但是当我关闭 GUI 时,我的线程 class 不会停止我通过 QThreadpool 调用的,有没有办法在我关闭 GUI 后正确停止工作线程? I already have an event loop but when I called it didn't do anything but when I use server class as a normal python threaded class it could close properly.我已经有一个事件循环,但是当我调用它时,它什么也没做,但是当我使用服务器 class 作为普通的 python 线程 class 时,它可以正确关闭。 Any help is appreciated, thanks in advance!任何帮助表示赞赏,在此先感谢!

Server code:服务器代码:

class Server(QRunnable):

def __init__(self, ip_port):
    super(Server, self).__init__()
    self._stop_event = threading.Event()

    self.ip_port = ip_port
    self.ip_addr = ""

    self.sckt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.broadcast = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)

    self.client_sock = None
    self.q_to_send = queue.Queue()

def get_ip_addr(self):
    pattern = r'inet (?:addr:)?(?!127\.0\.0\.1)((?:\d+\.){3}\d+)'
    p = subprocess.Popen(['ifconfig'], stdout=subprocess.PIPE)
    self.ip_addr = re.search(pattern, p.stdout.read().decode()).group(1)
    print("server ip", self.ip_addr)

def set_up_tcp(self):
    self.sckt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    self.sckt.settimeout(0.5)
    self.sckt.bind((self.ip_addr, self.ip_port))
    self.sckt.listen(5)

def broadcast_udp(self):
    self.broadcast.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    self.broadcast.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    self.broadcast.sendto(bytes(self.ip_addr, encoding='utf-8'), ("<broadcast>", self.ip_port))
    print("UDP broadcast sent!")

def connected(self):
    self.broadcast.close()
    self.client_sock.setblocking(False)

def stop(self):
    self._stop_event.set()
    print("Stopping server thread...")

def stopped(self):
    return self._stop_event.is_set()

def run(self):
    self.get_ip_addr()
    self.set_up_tcp()
    while not self.stopped():

        while True:
            self.broadcast_udp()
            time.sleep(1)
            try:
                self.client_sock, _ = self.sckt.accept()
                print("\nA client has connected!")
                break
            except socket.timeout:
                print("socket timeout")
                pass
        self.connected()
        print("Switched to TCP")

        while True:
            try:
                time.sleep(0.01)
                if self.q_to_send.empty():
                    pass
                else:
                    msg = self.q_to_send.get()
                    self.client_sock.send(bytes(msg, "utf8"))
                    print("'" + msg + "'" + " sent")
            except BrokenPipeError:
                print("\nClient disconnected! Now broadcasting UDP")
                break

GUI code:图形用户界面代码:

class Ui_MainWindow(QMainWindow):
def __init__(self):
    super(Ui_MainWindow, self).__init__()

    MainWindow.setObjectName("MainWindow")
    MainWindow.resize(470, 319)
    self.centralwidget = QtWidgets.QWidget(MainWindow)

    self.centralwidget.setObjectName("centralwidget")
    self.sendButton = QtWidgets.QPushButton(self.centralwidget)
    self.sendButton.setGeometry(QtCore.QRect(180, 200, 112, 32))

    self.sendButton.setObjectName("sendButton")
    self.label = QtWidgets.QLabel(self.centralwidget)
    self.label.setGeometry(QtCore.QRect(140, 30, 241, 20))

    self.label.setObjectName("label")
    self.textEditBox = QtWidgets.QTextEdit(self.centralwidget)
    self.textEditBox.setGeometry(QtCore.QRect(120, 90, 231, 71))

    self.textEditBox.setObjectName("textEditBox")
    MainWindow.setCentralWidget(self.centralwidget)
    self.menubar = QtWidgets.QMenuBar(MainWindow)
    self.menubar.setGeometry(QtCore.QRect(0, 0, 470, 22))

    self.menubar.setObjectName("menubar")
    MainWindow.setMenuBar(self.menubar)
    self.statusbar = QtWidgets.QStatusBar(MainWindow)
    self.statusbar.setObjectName("statusbar")
    MainWindow.setStatusBar(self.statusbar)

    self.retranslateUi(MainWindow)
    QtCore.QMetaObject.connectSlotsByName(MainWindow)

    #init UI
    self.initUI()

    #start server
    self.server = Server(10000)
    self.threadpool = QThreadPool()
    self.threadpool.start(self.server)

def initUI(self):
    self.sendButton.clicked.connect(self.send)

def retranslateUi(self, MainWindow):
    _translate = QtCore.QCoreApplication.translate
    MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
    self.sendButton.setText(_translate("MainWindow", "Send"))
    self.label.setText(_translate("MainWindow", "Enter command below to send"))

def send(self):
    self.server.q_to_send.put(self.textEditBox.toPlainText())

if __name__ == "__main__":
import sys

app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
MainWindow.show()
app.exec_()

why not, on your way out such as in QMainWindow.closeEvent(), tell you server to stop: self.server.q_to_send.put(TIME_IS_UP_LET_US_GET_OFF_WORK) or self.server.stop()为什么不呢,在你离开的时候,比如在 QMainWindow.closeEvent() 中,告诉你服务器停止:self.server.q_to_send.put(TIME_IS_UP_LET_US_GET_OFF_WORK) 或 self.server.stop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM