解析Qt元对象系统(五) Q_INVOKABLE与invokeMethod(automatic connection从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程)good

概述
查看Qt源码可知,Q_INVOKABLE是个空宏,目的在于让moc识别。
使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起。
Q_INVOKABLE与QMetaObject::invokeMethod均由元对象系统唤起。这一机制在Qt C++/QML混合编程,跨线程编程,Qt Service Framework 以及 Qt/ HTML5混合编程以及里广泛使用。

QMetaObject::invokeMethod的声明如下,还有几个inline重载:

static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
...... //后面还有9个参数
1
2
3
4
5
函数为静态函数,用法是尝试调用对象obj的方法member(注意member可以为信号或者是槽),如何member可以被调用,则返回真,否则返回假。QMetaObject::invokeMethod可以是异步调用,也可以是同步调用。这取决与它的连接方式Qt::ConnectionType type。如果type为Qt::DirectConnection,则为同步调用,若为Qt::QueuedConnection,则为异步调用。

调用机制
direct connection 是指:发起信号的线程会直接触发其所连接的槽;
queued connection 是指:一个事件被派发到接收者所在的线程中,在这里,事件循环会之后的某一时间将该事件拾起并引起槽的调用;
blocking queued connection 与queued connection的区别在于,发送者的线程会被阻塞,直至接收者所在线程的事件循环处理发送者发送(入栈)的事件,当连接信号的槽被触发后,阻塞被解除;
automatic connection (缺省默认参数) 是指: 如果接收者所依附的线程和当前线程是同一个线程,direct connection会被使用。否则使用queued connection。
从Qt4.8开始的解释已经与之前不同,发送对象驻足于哪一个线程并不重要,发送者对象本身在哪一个线程对与信号槽连接类型不起任何作用,起到决定作用的是接收者对象所驻足的线程以及发射信号(该信号与接受者连接)的线程是不是在同一个线程。
看源码,发现invokeMethod最后调用QMetaMethod::invoke,在这里检查连接类型:

// 处理自动连接的情况
QThread *currentThread = QThread::currentThread();
QThread *objectThread = object->thread();
if (connectionType == Qt::AutoConnection) {
connectionType = currentThread == objectThread
? Qt::DirectConnection
: Qt::QueuedConnection;
}
1
2
3
4
5
6
7
8
对直接连接,直接调 metacall,它进而去调用对象的qt_metacall:

if (connectionType == Qt::DirectConnection) {
return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0;
1
2
对于队列连接,实际用的是postEvent派发了一个 QMetaCallEvent事件,也就是异步调用,这才能实现跨线程发送事件:

if (connectionType == Qt::QueuedConnection)
{ . . . . . .
QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
0, -1, nargs, types, args));
}
1
2
3
4
5
常用的形式
QMetaObject::invokeMethod(object, "methodName", Qt::QueuedConnection,
Q_ARG(type1, arg1),
Q_ARG(type2, arg2));
1
2
3
还有一种是没有第三个参数,也就是不指明信号与槽的连接方式。

请注意,因为上面所示的参数需要被在构建事件时进行硬拷贝,参数的自定义型别所对应的类需要提供一个共有的构造函数、析构函数以及拷贝构造函数。而且必须使用注册Qt型别系统所提供的qRegisterMetaType() 方法来注册这一自定义型别。
---------------------
作者:SilentAssassin
来源:CSDN
原文:https://blog.csdn.net/yao5hed/article/details/81142365?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!

 之前我就问过,thread对象本身属于主线程(没有使用moveToThread),它使用自己的成员变量对象(所以也属于主线程)发射信号(哪怕是在run()函数里发射),它所连接的槽函数是主线程里的函数,那么这个函数是异步执行,还是同步执行?根据这篇文章上的答案是,同步执行 
上一篇:苹果Mac图片无损放大软件:ON1 Resize


下一篇:走过路过不要错过 包你一文看懂支撑向量机SVM