【问题标题】:Do signals need to be protected by a QMutex?信号是否需要由 QMutex 保护?
【发布时间】:2024-05-30 03:25:02
【问题描述】:

我正在开发一个带有 PyQt5 和 2 个 QThreads 的 GUI,它们将使用各自的信号以及用于将错误代码发送回 GUI 的共享信号。我知道 QThreads 是要与 pyqtSignals 一起使用的,但是除了您从两个不同的线程发出共享信号之外,它们的行为是否与信号有关? 另外,在共享信号上使用 Qmutex 会确保线程同时访问它还是在处理信号时没用?

我编写了这个示例代码,它运行正常,但我不确定如何处理信号:

from PyQt5.QtCore import QObject, pyqtSignal, QThread, QCoreApplication
import time
import sys


class Class2(QThread):
    def __init__(self, signal):
        super().__init__()
        self.signal2 = signal

    def run(self):
        self.signal2.emit("Class 2 signal emitted")


class Class1(QThread):
    def __init__(self, signal):
        super().__init__()
        self.signal1 = signal

    def run(self):
        self.signal1.emit("Class 1 signal emitted")


class Action(QObject):
    shared_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        class1 = Class1(self.shared_signal)
        class2 = Class2(self.shared_signal)
        self.shared_signal.connect(self.action)
        class1.start()
        class2.start()
        time.sleep(1)

    def action(self, buffer):
        print(buffer)


app = QCoreApplication([])
Action = Action()
sys.exit(app.exec_())

感谢您的帮助!

【问题讨论】:

    标签: python python-3.x pyqt5 signals-slots qthread


    【解决方案1】:

    信号本身是线程安全的,因为它们的主要任务是将信息排入队列并且为此使用互斥体。

    
                    ┌----------------------┐
                    |                      |
                    |         QUEUE        |
                    |                      |
                    └----------------------┘
                      ▲ ▲ ... ▲   | |     |
                      | |     |   ▼ ▼ ... ▼
                       SIGNALS      SLOTS
    
    

    连接可能不是线程安全的,因为它取决于连接的类型。我建议您阅读 the docs 以了解哪种连接是不安全的(例如在不同线程上的 QObject 之间使用 Qt::DirectConnection)。


    在您的情况下,连接是安全的,另一方面,我认为 time.sleep 是不必要的,但这可能会导致对象在调用信号之前被破坏,因此可能的解决方案是:

    import sys
    
    
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QThread
    
    
    class Class2(QThread):
        def __init__(self, signal, parent=None):
            super().__init__(parent)
            self.signal2 = signal
    
        def run(self):
            self.signal2.emit("Class 2 signal emitted")
    
    
    class Class1(QThread):
        def __init__(self, signal, parent=None):
            super().__init__(parent)
            self.signal1 = signal
    
        def run(self):
            self.signal1.emit("Class 1 signal emitted")
    
    
    class Action(QObject):
        shared_signal = pyqtSignal(str)
    
        def __init__(self):
            super().__init__()
            class1 = Class1(self.shared_signal, self)
            class1.finished.connect(class1.deleteLater)
    
            class2 = Class2(self.shared_signal, self)
            class2.finished.connect(class2.deleteLater)
    
            self.shared_signal.connect(self.action)
            class1.start()
            class2.start()
    
        @pyqtSlot(str)
        def action(self, buffer):
            print(buffer)
    
    
    def main():
    
        app = QCoreApplication([])
        action = Action()
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    【讨论】:

    • 首先,感谢文档非常有帮助,也感谢您所做的编辑。我实际上需要帮助理解其中一个,为什么在创建线程实例时添加parent=Nonesuper().__init__(parent) 并传递 Action 实例?再次感谢!