【问题标题】:Qt: handling exceptions thrown from handlers of background threadsQt:处理后台线程处理程序抛出的异常
【发布时间】:2014-08-07 20:17:58
【问题描述】:

我知道您需要重新实现 QApplication::notify() 方法以正确捕获从主线程的事件处理程序抛出的异常。 但是其他线程呢?比如说,我有一个带槽的对象,这个对象存在于一个 QThread 中(使用默认的 run() 方法,它只调用 exec()),即这个对象的线程亲和性是后台 QThread。那么,我应该在哪里捕获从这个对象的插槽抛出的异常?

IOW,如何重新实现后台线程的 notify() 方法?

【问题讨论】:

  • 可能重载 exec 并放一个 try() { QThread::exec()} catch(...) { }
  • @drescherjm 你的意思是从插槽抛出的异常将“飞出” exec() 方法?它似乎对我不起作用。
  • 是的,我希望它可以工作。对不起。我对此一无所知,但订阅了。
  • @drescherjm : 你不能重载 QThread::exec,这个方法不是虚拟的
  • 谢谢。这就解释了为什么这不起作用..

标签: c++ multithreading qt exception-handling qt4


【解决方案1】:

当您使用重写的通知方法创建自定义应用程序时;您创建的 QThread 也使用此覆盖方法(作为主线程)一旦它开始了自己的事件循环

这意味着在实践中,如果您将任何插槽连接到 QThread::started 信号;然后这个槽在线程的事件循环之外执行,因此不在被覆盖的通知方法中。

这是一个有助于理解发生了什么的代码示例:

#include "mainwindow.h"
#include <QApplication>

#include <QDebug>
#include <QThread>
#include <QTimer>
#include <exception>

class ThrowingObject : public QObject
{
public:
    void doThrowInNotify()
    {
        qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing";
        throw std::exception("KBOOOM");
    }

    void scheduleThrow()
    {
        QTimer* singleShot = new QTimer(this);
        singleShot->setSingleShot(true);

        connect(singleShot, &QTimer::timeout, this, &ThrowingObject::doThrow);

        singleShot->start();

        qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I will throw in run";
    }

    void doThrow()
    {
        qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing right now";

        //This exception is not catched, and definitly crash the process
        throw std::exception("KBOOOM");
    }

    void doThrowOutsideNotify()
    {
        //wait 5s for demo purpose, this slot is called by Object2, after Object1 throw in thread1 event loop
        QThread::sleep(5);
        qDebug() << "I am execution on thread id" << QThread::currentThreadId() << " and I am throwing right now";

        //This exception is not catched, and definitly crash the process
        throw std::exception("FATAL KBOOOM");
    }

};

class ApplicationWithExceptionCatchedInNotify : public QApplication
{
public:
    ApplicationWithExceptionCatchedInNotify(int argc, char *argv[]) :
        QApplication(argc,argv)
    {}

    bool notify(QObject* receiver, QEvent *e) override
    {
        try {
            return QApplication::notify(receiver, e);
        }
        catch(std::runtime_error e)
        {
            qDebug() << "std::runtime_error in thread : " << QThread::currentThreadId();
            qDebug() << e.what();
        }
        catch(std::exception e)
        {
            qDebug() << "std::exception in thread : " << QThread::currentThreadId();
            qDebug() << e.what();
        }
        catch(...)
        {
            qDebug() << "exception thread : " << QThread::currentThreadId();
        }

        qDebug() << "catch in notify ";
        return false;
    }

};



int main(int argc, char *argv[])
{
ApplicationWithExceptionCatchedInNotify app(argc, argv);

qDebug() << "Main QThread id" << QThread::currentThreadId();

//Object o1 will throw in its event loop (in notify)
QThread thread1;
ThrowingObject o1;
o1.moveToThread(&thread1);

QObject::connect(&thread1, &QThread::started, &o1, &ThrowingObject::scheduleThrow);

thread1.start();

//Object o2 will throw before the event loop is installed
QThread thread2;
ThrowingObject o2;
o2.moveToThread(&thread2);
//Connect to started signal.
QObject::connect(&thread2, &QThread::started, &o2, &ThrowingObject::doThrowOutsideNotify);
thread2.start();

app.exec();
}

在 windows 上运行此代码示例,带有 Qt creator 的 Qt 5.9 给出了示例:

输出:

Main QThread id 0x11e4
I am execution on thread id 0x180c  and I will throw in run
I am execution on thread id 0x180c  and I am throwing right now
std::exception in thread :  0x180c
KBOOOM
catch in notify 
I am execution on thread id 0x27b8  and I am throwing right now

然后弹出一个 Microsoft Visual Studio 运行时库:

Microsoft Visual C++ Runtime Library

Debug Error!

Program: ...ad-Desktop_Qt_5_9_2_MSVC2017_64bit-Debug\debug\DemoThread.exe

abort() has been called

(Press Retry to debug the application)

如果你放断点;曾经可以意识到:

对象 o1 在 QThread::exec 方法中抛出,沿着调用堆栈我们可以看到我们在 ApplicationWithExceptionCatchedInNotify::Notify

1 ThrowingObject::doThrow main.cpp 35 0x7ff66615352b 2 QtPrivate::FunctorCall,QtPrivate::List,void,void (__cdecl ThrowingObject:: *)(void) __ptr64>::call qobjectdefs_impl.h 136 0x7ff66615358c 3 QtPrivate::FunctionPointer::call,void> qobjectdefs_impl.h 170 0x7ff666152ce7 4 QtPrivate::QSlotObject,void>::impl qobject_impl.h 121 0x7ff66615363e 5 QtPrivate::QSlotObjectBase::call qobject_impl.h 101 0x54a82428
6 QMetaObject::activate qobject.cpp 3754 0x54a70ee0
7 QMetaObject::激活 qobject.cpp 3629 0x54a707a8
8 QTimer::timeout moc_qtimer.cpp 202 0x54a8f739
9 QTimer::timerEvent qtimer.cpp 257 0x54a8f79a
10 QObject::event qobject.cpp 1228 0x54a72b73
11 QApplicationPrivate::notify_helper qapplication.cpp 3722 0x53aeb8ee
12 QApplication::notify qapplication.cpp 3094 0x53ae6323
13 ApplicationWithExceptionCatchedInNotify::notify main.cpp 60 0x7ff666158730 14 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1018 0x54a1b0c6
15 QCoreApplication::sendEvent qcoreapplication.h 233 0x54a26062
16 QEventDispatcherWin32::事件 qeventdispatcher_win.cpp 1041 0x54ad8cab
17 QApplicationPrivate::notify_helper qapplication.cpp 3722 0x53aeb8ee
18 QApplication::notify qapplication.cpp 3094 0x53ae6323
19 ApplicationWithExceptionCatchedInNotify::notify main.cpp 60 0x7ff666158730 20 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1018 0x54a1b0c6
21 QCoreApplication::sendEvent qcoreapplication.h 233 0x54a26062
22 QCoreApplicationPrivate::sendPostedEvents qcoreapplication.cpp 1678 0x54a1c982
23 QEventDispatcherWin32::sendPostedEvents qeventdispatcher_win.cpp 1064 0x54ad8e6a
24 qt_internal_proc qeventdispatcher_win.cpp 237 0x54ad6b47
25 CallWindowProcW USER32 0x7ffba1571c24 26 DispatchMessageW USER32 0x7ffba157156c 27 QEventDispatcherWin32::processEvents qeventdispatcher_win.cpp 628 0x54ad755b
28 QEventLoop::processEvents qeventloop.cpp 135 0x54a15498
29 QEventLoop::exec qeventloop.cpp 212 0x54a156de
30 QThread::exec qthread.cpp 515 0x5465028f
31 QThread::run qthread.cpp 583 0x546501c3
32 QThreadPrivate::start qthread_win.cpp 380 0x5465caed
33 BaseThreadInitThunk 内核32 0x7ffb9f5b8364 34 RtlUserThreadStart ntdll 0x7ffba1bb7091

Object o2 在 QThread::start 方法中抛出;在 thread2 事件循环之外

1 ThrowingObject::doThrowOutsideNotify main.cpp 45 0x7ff666152ba6 2 QtPrivate::FunctorCall,QtPrivate::List,void,void (__cdecl ThrowingObject:: *)(void) __ptr64>::call qobjectdefs_impl.h 136 0x7ff66615358c 3 QtPrivate::FunctionPointer::call,void> qobjectdefs_impl.h 170 0x7ff666152ce7 4 QtPrivate::QSlotObject,void>::impl qobject_impl.h 121 0x7ff66615363e 5 QtPrivate::QSlotObjectBase::call qobject_impl.h 101 0x54a82428
6 QMetaObject::activate qobject.cpp 3754 0x54a70ee0
7 QMetaObject::activate qobject.cpp 3629 0x54a707a8
8 QThread::started moc_qthread.cpp 160 0x54650149
9 QThreadPrivate::start qthread_win.cpp 377 0x5465cad6
10 BaseThreadInitThunk 内核32 0x7ffb9f5b8364 11 RtlUserThreadStart ntdll 0x7ffba1bb7091

【讨论】:

    【解决方案2】:

    由于某些版本的 Qt5,QCoreApplication::notify 手册说:

    这个函数被所有发送到任意对象的事件调用 线程。

    所以这意味着我最初的问题的正确答案是“做你一直在做的事情”。

    有趣的是,当我发现这一点时,这些新发现的知识立即被同一手册中的这句话所过时:

    这个函数不会被存在于 Qt 6 中的主线程。需要该功能的应用程序应该 同时为他们的事件检查需求寻找其他解决方案。 更改可能会扩展到主线程,导致此功能 将被弃用。

    哈哈。我还没有迁移到 Qt6,所以我不知道那里的情况,但是当我这样做时,我可能会重新打开这个确切的问题。

    【讨论】:

      猜你喜欢
      • 2011-02-09
      • 1970-01-01
      • 2011-03-04
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多