线程:PyQt 因“出队时队列中的未知请求”而崩溃

Posted

技术标签:

【中文标题】线程:PyQt 因“出队时队列中的未知请求”而崩溃【英文标题】:Threading: PyQt crashes with "unknown request in queue while dequeuing" 【发布时间】:2015-08-11 22:00:15 【问题描述】:

我正在开发的应用程序的一部分需要向一小群人发送一些电子邮件。由于连接到 SMTP 服务器并发送电子邮件可能需要一些时间,因此我想在此操作期间提供一个进度条,使用后台线程来完成工作。

现在发生的情况是,我可以实现一个运行良好的测试结构,但是一旦我尝试从应用程序的后端创建一个对象以实际执行任何电子邮件操作,它就会完全崩溃(好像它段错误),将其转储到控制台:

[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
Aborted

我发现搜索这些错误的唯一相关线程说明了信号实现错误(对于 PySide,PySide and QProgressBar update in a different thread),但在我的情况下,信号工作完全正常,直到我尝试创建该对象(这不是t 完全基于 Qt 类)。

这是我的 GUI 代码的简化版本:

class SendingDialog(QtGui.QDialog):
    def __init__(self, parent, optsDict, cls, zid):
        QtGui.QDialog.__init__(self)
        self.form = Ui_Dialog()
        self.form.setupUi(self)
        # initialize some class variables...

        self.beginConnect()
        self.thread = WorkerThread()
        self.thread.insertOptions(self.opts, self.cls, self.zid)
        self.thread.finished.connect(self.endOfThread)
        self.thread.serverContacted.connect(self.startProgress)
        self.thread.aboutToEmail.connect(self.updateProgress)
        self.thread.start()

    def beginConnect(self):
        # start busy indicator

    def startProgress(self):
        # set up progress bar

    def updateProgress(self):
        # increment progress bar

    def endOfThread(self):
        self.thread.quit()
        self.reject()

class WorkerThread(QtCore.QThread):
    serverContacted = QtCore.pyqtSignal(name="serverContacted")
    aboutToEmail = QtCore.pyqtSignal(name="aboutToEmail")

    def insertOptions(self, opts, cls, zid):
        self.opts = opts
        self.cls = cls
        self.zid = zid

    def run(self):
        # upon running the following line, the application crashes.
        emailman = db.emailing.EmailManager(self.opts, self.cls, self.zid)

如果我将一些虚拟代码放入 run() 以休眠、发出适当的信号或打印测试值,一切正常;但是一旦我尝试实例化EmailManager,整个事情就会崩溃。

EmailManager 是从object 派生的一个不起眼的类,采用我给它的参数(opts 是一个字典,cls 是一种不同类型的类似不起眼的对象,zid 只是一个普通的数字)。构造函数如下所示:

def __init__(self, optsDict, cls, zid):
    self.opts = optsDict
    self.cls = cls
    self.historyItem = HistoryItem(zid)
    self.studentsList = studentsInClass(cls)
    self.connection = None

我正在根据参数构造其他几个对象,但除此之外,没有发生任何复杂或不寻常的事情。 db.emailing 模块中的代码根本没有使用 Qt 或线程。

我什至不知道如何开始调试它,因此非常感谢任何关于可能发生的事情或我如何尝试找出问题的建议。

编辑:如果它有帮助,这里是来自 gdb 的回溯(我对发生的事情知之甚少,觉得它有帮助):

Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7fffeb146700 (LWP 31150)]
0x00007ffff762acc9 in __GI_raise (sig=sig@entry=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56  ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff762acc9 in __GI_raise (sig=sig@entry=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007ffff762e0d8 in __GI_abort () at abort.c:89
#2  0x00007ffff7623b86 in __assert_fail_base (
    fmt=0x7ffff7774830 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", 
    assertion=assertion@entry=0x7ffff6a4420d "!xcb_xlib_unknown_req_in_deq", file=file@entry=0x7ffff6a441db "../../src/xcb_io.c", line=line@entry=179, 
    function=function@entry=0x7ffff6a446b0 "dequeue_pending_request")
    at assert.c:92
#3  0x00007ffff7623c32 in __GI___assert_fail (
    assertion=0x7ffff6a4420d "!xcb_xlib_unknown_req_in_deq", 
    file=0x7ffff6a441db "../../src/xcb_io.c", line=179, 
    function=0x7ffff6a446b0 "dequeue_pending_request") at assert.c:101
#4  0x00007ffff69d479c in ?? () from /usr/lib/x86_64-linux-gnu/libX11.so.6
#5  0x00007ffff69d55c3 in _XReply ()
   from /usr/lib/x86_64-linux-gnu/libX11.so.6
#6  0x00007ffff69bc346 in XGetWindowProperty ()
   from /usr/lib/x86_64-linux-gnu/libX11.so.6
#7  0x00007ffff69bb22e in XGetWMHints ()
   from /usr/lib/x86_64-linux-gnu/libX11.so.6
#8  0x00007ffff4c87c4b in QWidgetPrivate::setWindowIcon_sys(bool) ()
   from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#9  0x00007ffff4c38405 in QWidget::create(unsigned long, bool, bool) ()
   from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#10 0x00007ffff4c4086a in QWidget::setVisible(bool) ()
   from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#11 0x00007ffff509956e in QDialog::setVisible(bool) ()
   from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#12 0x00007ffff5c24b7c in ?? ()
   from /usr/lib/python2.7/dist-packages/PyQt4/QtGui.so
#13 0x00007ffff5099026 in QDialog::exec() ()
   from /usr/lib/x86_64-linux-gnu/libQtGui.so.4
#14 0x00007ffff5be5fb5 in ?? ()
   from /usr/lib/python2.7/dist-packages/PyQt4/QtGui.so
#15 0x000000000049968d in PyEval_EvalFrameEx ()
#16 0x00000000004a090c in PyEval_EvalCodeEx ()
#17 0x0000000000499a52 in PyEval_EvalFrameEx ()
#18 0x00000000004a1c9a in ?? ()
#19 0x00000000004dfe94 in ?? ()
#20 0x00000000004dc9cb in PyEval_CallObjectWithKeywords ()
#21 0x000000000043734b in PyErr_PrintEx ()
#22 0x00007ffff186fd4d in ?? ()
   from /usr/lib/python2.7/dist-packages/sip.so
#23 0x00007ffff14b2ece in ?? ()
   from /usr/lib/python2.7/dist-packages/PyQt4/QtCore.so
#24 0x00007ffff45be32f in ?? ()
   from /usr/lib/x86_64-linux-gnu/libQtCore.so.4
#25 0x00007ffff79c1182 in start_thread (arg=0x7fffeb146700)
    at pthread_create.c:312
#26 0x00007ffff76ee47d in clone ()
    at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

【问题讨论】:

如果您创建一个简单的测试应用程序并尝试在 Python 线程中使用EmailManager(使用threading 模块)会发生什么? 【参考方案1】:

哇,这太晦涩了。

X11 窗口函数显然不是线程安全的,除非明确设置为这样,并且无论出于何种原因,PyQt 都不会自动将它们设置为。这可以通过在 QApplication 构造函数之前添加以下内容来纠正:

QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)

请参阅QApplicationAttributes 上的文档。

【讨论】:

以上是关于线程:PyQt 因“出队时队列中的未知请求”而崩溃的主要内容,如果未能解决你的问题,请参考以下文章

使用 python 线程时,python 因核心转储而崩溃

PyQt5在进行任何交互时崩溃[重复]

PyQt5 多线程崩溃,没有控制台错误

PyQt5 QThread 不会因终止或标志变量而停止

PyQT5 多线程问题

PyQt5 - pyuic5 因 SIP 版本错误而失败