多线程:PyQt崩溃并显示“在出队时队列中存在未知请求”

8

我的一个正在开发的应用程序需要向一小群人发送一些电子邮件。由于连接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 和 QProgressBar 在不同线程中更新),它说信号被错误地实现了。但在我的情况下,直到我尝试创建那个对象(该对象根本不基于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

1
如果您创建一个简单的测试应用程序并尝试在Python线程中使用EmailManager(使用threading模块),会发生什么? - three_pineapples
1个回答

16

哇,这真是晦涩难懂。

显然,X11窗口函数在没有被明确设置的情况下不支持多线程,而且由于某种原因,PyQt没有自动设置它们。这可以通过在QApplication构造函数之前添加以下内容来进行纠正:

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

请参阅QApplicationAttributes的文档。


以上代码中的QApplication构造函数在哪里?我没有使用QApplication,所以我不确定如何/在哪里添加您提到的setAttribute调用。谢谢! - skittlebiz
@skittlebiz 嗯,如果你没有使用 QApplication,那么你是如何启动 Qt 事件循环的呢?我不知道还有其他方法。 - Soren Bjornstad
1
无论如何,我认为你只需要在初始化任何其他Qt内容之前调用它。你可以尝试在控制流的开头处调用它。 - Soren Bjornstad
1
我的评论有点离题了。我现在才意识到这个问题是关于PyQt的。我只是用标准的Python线程Thread流程遇到了“在出队时队列中存在未知请求”的崩溃,所以也许我应该完全提出另一个问题。 - skittlebiz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接