如何优雅地防止QThread被破坏或垃圾回收?

时间:2018-07-07 13:23:10

标签: python pyqt pyqt5

class CustomThread(QThread):
    def __init__(self, target, args=()):
        super().__init__()
        self.__target = target
        self.__args = args

    def run(self):
        self.__target(*self.__args)

class MyClass:
    def target(self):
        print("Hello world")
        # Do something
    def func(self):
        thread = CustomThread(self.target)
        thread.start()

在这种情况下,如果我调用MyClass的func(),则程序崩溃,表明qthread在运行时已被破坏。 (因为线程是局部变量) 因此,我将线程更改为self.thread,使其成为MyClass的成员变量,这样它就不会被破坏。

class CustomThread(QThread):
    def __init__(self, target, args=()):
        super().__init__()
        self.__target = target
        self.__args = args

    def run(self):
        self.__target(*self.__args)

class MyClass:
    def target(self):
        print("Hello world")
        # Do something
    def func(self):
        self.thread = CustomThread(self.target)
        self.thread.start()

但是,如果我两次或更多次调用func(),运行中的线程可能会被垃圾回收,并且程序将崩溃。 因此,我尝试通过添加列表并附加每个线程来解决此问题。

class CustomThread(QThread):
    def __init__(self, target, args=()):
        super().__init__()
        self.__target = target
        self.__args = args

    def run(self):
        self.__target(*self.__args)

class MyClass:
    def __init__(self):
        self.threads = []
    def target(self):
        print("Hello world")
        # Do something
    def func(self):
        self.thread = CustomThread(self.target)
        self.threads.append(self.thread)
        self.thread.start()

这解决了问题,但看起来效率低下且不成熟。 另外,我必须弹出每个完成任务的线程。 (也许我必须用信号和插槽来实现它,当然看起来有些多余)

如何更好地解决这个问题?

1 个答案:

答案 0 :(得分:1)

您的方法很好,但是由于线程需要定期检查以查看其是否完整,因此列表线程的维护可能很繁琐。幸运的是,QThread有一个finished信号,当线程完成时从Qt循环调用该信号。将您的线程列表更改为字典,然后使用唯一的线程ID标识退出的线程,将此信号连接到插槽以进行清理。

import time

class CustomThread(QThread):
    def __init__(self, target, callback, args=()):
        super().__init__()
        self.callback = callback
        self.__target = target
        self.__args = args
        self.finished.connect(lambda: self.callitback)
    def run(self):
        self.__target(*self.__args)
    def callitback(self):
        self.callback()


class MyClass:
    def __init__(self):
        self.threads = {}
    def target(self):
        print("Hello world")
        # Do something
    def threadFinished(self, threadId):
        self.threads.pop(threadId, None)
    def func(self):
        threadId = time.time() 
        thread = CustomThread(self.target, lambda tid=threadId: self.threadFinished(tid))
        self.threads[threadId] = thread
        thread.start()