线程:PyQt崩溃时出现"队列中的未知请求"

Sor*_*tad 7 multithreading pyqt4 python-2.7

我正在开发的应用程序的一部分需要向一小群人发送一些电子邮件.由于连接到SMTP服务器并发送电子邮件可能需要一段时间,因此我想在此操作期间提供进度条.我以前只使用过线程,所以使用QThread来做这件事对我来说有点困难.

现在发生的事情是我可以实现一个工作正常的测试结构,但是当我尝试从我的应用程序的后端创建一个对象来实际执行任何电子邮件操作时,它完全崩溃(好像它已经segfaulted) ,将其转储到控制台:

[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
Run Code Online (Sandbox Code Playgroud)

我发现搜索这些错误的唯一相关线程说的是错误实现的信号(对于不同线程中的 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 Code Online (Sandbox Code Playgroud)

如果我在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
Run Code Online (Sandbox Code Playgroud)

我正在根据参数构建其他几个对象,但除此之外,没有任何复杂或不寻常的事情发生.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
Run Code Online (Sandbox Code Playgroud)

Sor*_*tad 15

哇,这太模糊了.

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

QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
Run Code Online (Sandbox Code Playgroud)

请参阅有关QApplicationAttributes的文档.

对于它的价值,EmailManager现在在线程中启动时无法正常工作,因为它使用共享的SQLite数据库连接来构建其他对象,这与sqlite3库不相关.我现在也遇到了分段错误.但至少可以解决这个特殊问题.