【问题标题】:What's the proper way of catching an exception of method running in a different thread?捕获在不同线程中运行的方法异常的正确方法是什么?
【发布时间】:2015-01-21 07:50:48
【问题描述】:

我正在运行一个带有 C++ 插件的 qml 应用程序。申请很简单:

QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///ui/views/mainwindow.qml")));

return app.exec();

但是 qml 插件有很多代码。为了避免在 qml 中冻结,我通过 moveToThread() 将对象放入线程中,并使用 Qt::QueuedConnection 参数通过 QMetaObject::invokeMethod() 异步调用方法。问题是我通过invokeMethod 调用的方法可能会抛出异常,然后程序会因为我无法捕获它们而崩溃:

try {
    QMetaObject::invokeMethod(&qlNetwork, "disconnect", Qt::QueuedConnection);
} catch (const std::runtime_error& e) {
    emit error(e.what());
}

当然,这段代码不会起作用,因为调用是非阻塞的。问题是:那么我如何才能从不同线程(QThread)中的对象捕获异常?

【问题讨论】:

    标签: c++ multithreading qt exception qthread


    【解决方案1】:

    您将创建一个从另一个线程调用disconnect 并处理异常的包装槽。

    void ThisClass::wrapperMethod() {
        try {
            qlNetwork->disconnect();
        } catch (const std::runtime_error& e) {
            emit error(e.what());
        }
    }
    

    然后你异步调用包装器方法:

        QMetaObject::invokeMethod(this, "wrapperMethod", Qt::QueuedConnection);
    

    确保wrapperMethodSLOT 或者它被定义为Q_INVOKABLE 并且ThisClass 实例被移动到不同的线程。


    使用 lambdas 的可能解决方案

    QTimer *t = new QTimer();
    connect(t, &QTimer::timeout, this, [=]() {
        t->deleteLater();
        try {
            qlNetwork->disconnect();
        } catch (const std::runtime_error& e) {
            emit this->error(e.what());
        }
    }, Qt::QueuedConnection);
    /* don't forget to move the timer to the thread where
       you want the lambda to be executed*/
    t->moveToThread(targetThread);
    t->setSingleShot(true);
    t->start(0);
    

    在 QtConcurrent (Victor Polevoy) 中使用 lambda 的解决方案

    void ThisClass::performDisconnect() {
        QtConcurrent::run([this]() {
            try {
                this->qlNetwork.disconnect();
            } catch (const std::runtime_error& e) {
                emit error(e.what());
            }
        });
    }
    

    【讨论】:

    • 是的,我已经考虑过了,但是对于这种常见情况,这里没有通用的解决方案吗?
    • 通用解决方案是什么意思?异常必须在执行方法调用的同一线程中同步处理。
    • 我的意思是一个解决方案,它可以在没有任何包装器的情况下做到这一点。例如,QThread::run 的一些内部 qt 包装器会捕获 (...) 并发出一些信号或类似的东西。
    • 好的,我至少可以用 lambdas 做到这一点吗?我的尝试没有成功。
    • 我添加了 QtConcurrent 的解决方案,由我亲自测试。
    猜你喜欢
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 2012-10-10
    相关资源
    最近更新 更多