Как использовать QThread для передачи данных между дочерними потоками?

#python #multithreading #qthread #pyside2

#python #многопоточность #qthread #pyside2

Вопрос:

Я хочу использовать QThread в Pyside2 для передачи данных между дочерними потоками. Я хочу написать приложение, которое считывает последовательные данные в readThread (дочерний поток), записывает данные, прочитанные в writeThread (дочерний поток), и отображает последовательные данные в функции plot (основной поток). Я написал демо для этого, и код выглядит следующим образом:

 import threading
from PySide2.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout, QLCDNumber, QMessageBox
from PySide2.QtCore import *
import sys
import os

# add environment path of pyside2
envpath = r'E:anacondaenvstensorflowLibsite-packagesPySide2pluginsplatforms'
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = envpath

sec = 0    # counter 

# child thread, representing the process of read serial data
class ReadThread(QThread):
        timer = Signal()  # create a signal
        end = Signal()  # create a signal
    
        def run(self):
            print("ReadThread1", threading.current_thread())  # print thread ID
            while True:
                self.sleep(1)  # sleep 1s
                print("ReadThread2", threading.current_thread())  
                if sec == 2:
                    self.end.emit()  # emit end signal
                    break
                self.timer.emit()
    
# chiald thead2, represents write data to txt file
class WriteThread(QThread):
    
        def run(self):
            print("WriteThread", threading.current_thread())  # print thread ID
    
class Counter(QWidget):
    
        def __init__(self, parent=None):
            super(Counter, self).__init__(parent)
    
            self.setWindowTitle("qthreading test")
            self.resize(300, 120)
    
            # add a layout
            layout = QVBoxLayout()
            self.lcdNumber = QLCDNumber()
            layout.addWidget(self.lcdNumber)
            # add a pushbutton 
            button = QPushButton('start')
            layout.addWidget(button)
    
            self.readThread = ReadThread()  # workthread
            self.writeThread = WriteThread()  # writeThread
            self.readThread.timer.connect(self.plot)   
    
            self.readThread.timer.connect(self.writeThread.run) #I want to connect readThread and writeThread
    
            self.readThread.end.connect(self.end)
    
            button.clicked.connect(self.work)
    
            self.setLayout(layout)
    
        # The callback function of readThread (Main Thread), representing the process of plot serial data
        def plot(self):
            global sec
            sec  = 1
            self.lcdNumber.display(sec)
            print("countTime", threading.current_thread())  
    
        def end(self):
            QMessageBox.information(self, "message", "stop")
    
        # start readTread and WriteThread when button is clicked
        def work(self):
            self.readThread.start()
            self.writeThread.start()  
    
    
if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Counter()
    form.show()
    sys.exit(app.exec_())
 

Когда я запустил программу, результат был следующим:

 **ReadThread1 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_DummyThread(Dummy-2, started daemon 2552)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_MainThread(MainThread, started 7536)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_MainThread(MainThread, started 7536)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>**
 

Как вы можете видеть, сначала и readThread, и writeThread находятся в дочернем потоке, но при отправке сигнала () поток записи запускается в основном потоке, в то время как поток чтения находится в дочернем потоке. Я не знаю, что не так с моими кодами. Почему операция не выполняется в дочернем потоке, когда writeThread отвечает на signal() ? Было бы неплохо, если бы вы могли изменить его непосредственно в моем коде.

Ответ №1:

В вашей логике есть основная ошибка: вы не должны напрямую вызывать метод run QThread, поскольку он косвенно вызывается методом start, поэтому вы замечаете, что вначале метод «run» выполняется во вторичном потоке, а затем, когда вы вызываете его напрямую, он будет выполненвыполняется в потоке, к которому принадлежит QObject (WriteThread), который является основным потоком.

Если вы хотите выполнить задачу во вторичном потоке в зависимости от сигнала, вы должны создать QObject и переместить его в новый поток:

 class ReadThread(QThread):
    timer = Signal()  # create a signal
    end = Signal()  # create a signal

    def run(self):
        print("ReadThread1", threading.current_thread())  # print thread ID
        while True:
            self.sleep(1)  # sleep 1s
            print("ReadThread2", threading.current_thread())
            if sec == 2:
                self.end.emit()  # emit end signal
                break
            self.timer.emit()


class WriteWorker(QObject):
    def run_task(self):
        print("WriteThread", threading.current_thread())


class Counter(QWidget):
    def __init__(self, parent=None):
        super(Counter, self).__init__(parent)

        self.setWindowTitle("qthreading test")
        self.resize(300, 120)

        # add a layout
        layout = QVBoxLayout()
        self.lcdNumber = QLCDNumber()
        layout.addWidget(self.lcdNumber)
        # add a pushbutton
        button = QPushButton("start")
        layout.addWidget(button)

        self.readThread = ReadThread()  # workthread
        self.writeThread = QThread()  # writeThread
        self.readThread.timer.connect(self.plot)

        self.worker = WriteWorker()
        self.worker.moveToThread(self.writeThread)

        self.readThread.timer.connect(self.worker.run_task)

        self.readThread.end.connect(self.end)

        button.clicked.connect(self.work)

        self.setLayout(layout)

    def plot(self):
        global sec
        sec  = 1
        self.lcdNumber.display(sec)
        print("countTime", threading.current_thread())

    def end(self):
        QMessageBox.information(self, "message", "stop")

    def work(self):
        self.readThread.start()
        self.writeThread.start()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Counter()
    form.show()
    ret = app.exec_()
    form.writeThread.quit()
    form.writeThread.wait()
    sys.exit(ret)
 

Вывод:

 ReadThread1 <_DummyThread(Dummy-1, started daemon 140088050697792)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>
WriteThread <_DummyThread(Dummy-2, started daemon 140088042305088)>
countTime <_MainThread(MainThread, started 140088409069376)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>
countTime <_MainThread(MainThread, started 140088409069376)>
WriteThread <_DummyThread(Dummy-2, started daemon 140088042305088)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>