You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6中QRunnable设setAutoDelete(False)引发内存泄漏问题咨询

关于QRunnable与QThreadPool的生命周期问题解答

疑问1:设置task.setAutoDelete(False)是否会导致内存泄漏?

  • 在PySide6 6.5.2/6.6.2版本中,若未保留Python层对Task对象的引用,确实会出现内存泄漏:QThreadPool的C实现会持续持有QRunnable的C对象引用,导致对应的Python包装器无法被垃圾回收器(GC)回收,即使手动调用gc.collect()或程序关闭也无法触发__del__方法。
  • 在PyQt6.6.1中无此问题:无论autoDelete设为True或False,只要Python层的引用被释放,GC就能正常回收对象,触发__del__。
  • 若主动保留Python层的Task对象引用(比如将task存储在全局容器中),在PyQt6.6.1中主动释放引用后可正常回收;但在PySide6的上述版本中,即使保留引用后再释放,仍无法触发__del__,说明C++端的引用未正确释放。

疑问2:当设置task.setAutoDelete(True)时,QThreadPool应该删除QRunnable的C++对象还是Python包装器?

  • 从Qt的C原生逻辑来说,setAutoDelete(True)时,**QThreadPool会负责删除C的QRunnable对象**。
  • 在Python绑定层:
    • C对象被删除后,对应的Python包装器会失去底层的C实例支撑,随后会被Python的GC自动回收,触发__del__方法。
    • PyQt和PySide的绑定实现有差异:PyQt会更严谨地同步C对象与Python包装器的生命周期,而PySide6在autoDelete=False时,C端的QThreadPool未正确释放对QRunnable对象的引用,导致Python包装器被“挂起”无法回收。

复现验证说明

以下是复现代码(未保留Task引用):

import gc
import sys
import time

from PySide6.QtCore import QRunnable, QThreadPool
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout


class Task(QRunnable):
    def __init__(self):
        super().__init__()

    def __del__(self):
        print('Task.__del__')

    def run(self):
        print('Task.run')
        time.sleep(3)
        print(f'Task.run finished')


def run_task():
    task = Task()
    task.setAutoDelete(False)  # 注释此行则__del__正常触发
    QThreadPool().globalInstance().start(task)


def collect_by_gc():
    collected_count = gc.collect()
    print(f'collected by GC: {collected_count}')


def main():
    app = QApplication(sys.argv)

    run_task_push_button = QPushButton('Run Task')
    run_task_push_button.clicked.connect(run_task)

    collect_by_gc_push_button = QPushButton('Collect by GC')
    collect_by_gc_push_button.clicked.connect(collect_by_gc)

    layout = QVBoxLayout()
    layout.addWidget(run_task_push_button)
    layout.addWidget(collect_by_gc_push_button)

    window = QWidget()
    window.setWindowTitle('Test App')
    window.setLayout(layout)
    window.show()

    app.exec()


if __name__ == '__main__':
    main()
  • 测试结果:点击“Run Task”后,任务执行完成但__del__未被调用,手动触发GC也无效;将setAutoDelete(False)注释后,__del__会正常触发。
  • 测试环境:Windows10(x64)、Python3.10.12

内容的提问来源于stack exchange,提问作者BlackHoleTracer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 22:25:39