捕获在不同线程中运行的方法异常的正确方法是啥?
Posted
技术标签:
【中文标题】捕获在不同线程中运行的方法异常的正确方法是啥?【英文标题】: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)中的对象捕获异常?
【问题讨论】:
【参考方案1】:您将创建一个从另一个线程调用disconnect
并处理异常的包装槽。
void ThisClass::wrapperMethod()
try
qlNetwork->disconnect();
catch (const std::runtime_error& e)
emit error(e.what());
然后你异步调用包装器方法:
QMetaObject::invokeMethod(this, "wrapperMethod", Qt::QueuedConnection);
确保wrapperMethod
是SLOT
或者它被定义为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
的解决方案,由我亲自测试。以上是关于捕获在不同线程中运行的方法异常的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章