为啥从线程执行方法时使用 QMetaObject::invokeMethod
Posted
技术标签:
【中文标题】为啥从线程执行方法时使用 QMetaObject::invokeMethod【英文标题】:Why using QMetaObject::invokeMethod when executing method from thread为什么从线程执行方法时使用 QMetaObject::invokeMethod 【发布时间】:2012-12-19 08:35:52 【问题描述】:我有以下代码:
class A : public QObject
Q_OBJECT
public:
A() : QObject()
moveToThread(&t);
t.start();
~A()
t.quit();
t.wait();
void doSomething()
QMetaObject::invokeMethod(this,"doSomethingSlot");
public slots:
void doSomethingSlot()
//do something
emit ready();
signals:
void ready();
private:
QThread t;
为什么来自doSomething
的问题必须通过QMetaObject::invokeMethod
调用。我知道有一些与连接类型有关的东西。 有人能解释一下幕后是什么吗?
【问题讨论】:
ecatmur 给了你完美的答案。您还有另一个问题,将线程作为移动到该线程的对象的成员是非常糟糕的主意,在销毁期间(使用 deleteLater 时)您可能会遇到奇怪的问题。 完整解释请阅读this document @MarekR:有什么问题?我从***.com/questions/13878745/… 得到了这个解决方案 delateLater 在适当的线程中调用析构函数。当您销毁它时,您还将尝试销毁线程。所以代码会让线程保持活动状态并且代码会尝试销毁线程:它会挂起。所以这个解决方案只会破坏 delateLater 的功能。deleteLater
在哪里使用?
【参考方案1】:
由于您没有指定Qt::ConnectionType
,因此该方法将被调用为Qt::AutoConnection
,这意味着如果对象的线程亲和性是当前线程,它将被同步调用(就像正常的函数调用一样),否则异步。 “异步”是指构造一个QEvent
并将其推送到消息队列中,并在事件循环到达时处理。
如果接收对象可能在另一个线程中,则使用QMetaObject::invokeMethod
的原因是尝试直接在另一个线程中的对象上调用插槽可能会导致损坏或更糟,如果它访问或修改非线程安全数据.
【讨论】:
你的意思是如果我从另一个线程使用 A.doSomething() 可能会导致损坏?为什么这样?你能描述一下这个机制吗? @krzych 例如,如果您在另一个线程正在修改它的同时从 QString 读取,那么它可能看起来处于不一致的状态,从而导致间接无效指针。【参考方案2】:我喜欢这个技巧:
void A:doSomethingSlot()
if (thread()!=QThread::currentThread())
QMetaObject::invokeMethod(this,"doSomethingSlot", Qt::QueuedConnection);
return;
// this is done always in same thread
...
emit ready();
【讨论】:
不要那样做,使用编译时检查:QMetaObject::invokeMethod(this, [this] doSomethingSlot(); , Qt::QueuedConnection);
以上是关于为啥从线程执行方法时使用 QMetaObject::invokeMethod的主要内容,如果未能解决你的问题,请参考以下文章
如果我从线程调用 QMetaObject::invokeMethod 到单音,调用是不是仍在该 qthread 中?