QtinvokeMethod异步调⽤
概述
程序中,我们经常会调⽤函数,如果调⽤的函数耗时较长,同步调⽤会造成主程序的堵塞。Qt中提供了⼀个便捷的函
数QMetaObject::invokeMethod,⽅便我们异步调⽤,从⽽解决这⼀问题。本⽂只要讲述QMetaObject::invokeMethod的使⽤⽅法。
喇叭读音
函数原型
bool QMetaObject::invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(nullptr),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
中秋节的QGenericArgument val7 = QGenericArgument(),
打篮球膝盖疼
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
此函数⽤于调⽤对象的成员(信号或插槽)。如果可以调⽤成员,则返回true。如果没有此类成员或参数不匹配,则返回fal。QMetaObject::invokeMethod除上⽂这个函数以外还有5个重载函数,这⾥不再赘述。
参数说明:
obj:被调⽤对象的指针
member:成员⽅法的名称
type:连接⽅式,默认值为 Qt::AutoConnection
Qt::DirectConnection,则会⽴即调⽤该成员。(同步调⽤)
Qt::QueuedConnection,则会发送⼀个QEvent,并在应⽤程序进⼊主事件循环后⽴即调⽤该成员。(异步调⽤)
Qt::BlockingQueuedConnection,则将以与Qt :: QueuedConnection相同的⽅式调⽤该⽅法,除了当前线程将阻塞直到事件被传递。使⽤此连接类型在同⼀线程中的对象之间进⾏通信将导致死锁。(异步调⽤)
Qt::AutoConnection,则如果obj与调⽤者位于同⼀个线程中,则会同步调⽤该成员; 否则它将异步调⽤该成员。
ret:接收被调⽤函数的返回值
val0~val9:传⼊被调⽤函数的参数,最多⼗个参数
注:必须要使⽤Q_RETURN_ARG()宏来封装函数返回值、Q_ARG()宏来封装函数参数。
⽰例
若⼀个对象obj有⼀个槽函数func(QString,int),返回值为bool,那么调⽤⽅式如下:
bool result;
//同步调⽤
QMetaObject::invokeMethod(obj, "func", Qt::DirectConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(QString, "test"),
Q_ARG(int, 100);
//异步调⽤
QMetaObject::invokeMethod(obj, "func", Qt::QueuedConnection,
Q_ARG(QString, "test"),
Q_ARG(int, 100);
注:使⽤Qt::QueuedConnection异步调⽤,将⽆法获取返回值,因为此连接⽅式只是负责把事件交给事件队列,然后⽴刻返回,所以,函数返回值就⽆法确定了。
但,我们可以使⽤上⽂提及的Qt::BlockingQueuedConnection连接⽅式,这个连接⽅式会阻塞发射信号的线程⼀直等到队列连接槽返回后,才会恢复阻塞,这样就可以保证我们能得到函数的返回值。使⽤如下:
bool result;
QMetaObject::invokeMethod(obj, "func", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(QString, "test"),8画属木的字
Q_ARG(int, 100);
需要注意的是,qt官⽅⽂档中的提醒:使⽤此连接类型在同⼀线程中的对象之间进⾏通信将导致死锁
最后,因为连接⽅式type默认值为Qt::AutoConnection,所以当被调⽤的obj与调⽤者不在同⼀线程中,可以直接调⽤:
//此Tcprver为⼀个独⽴线程
会计专业实习日志//在主程序中调⽤reportImageResult(),因为TcpServer对象与调⽤者主线程不在同⼀线程中。
//Qt::AutoConnection此连接⽅式将会⾃动以异步的⽅式调⽤
void TcpServer::reportImageResult(int imgId, const QImage &image, int result)
{
QMetaObject::invokeMethod(this, "reportImageResultAsync",
父母呼Q_ARG(int, imgId),
Q_ARG(QImage, image),
Q_ARG(int, result);
}
小狗断尾
进⼀步讨论
使⽤QMetaObject::invokeMethod来调⽤函数时,当函数的参数有⾃定义类型时,程序将会报错,因为调⽤的类型必须是信号、槽,以及Qt 元对象系统能识别的类型。可以使⽤Qt命名类型所提供的qRegisterMetaType()来注册⾃定义类型。
⽰例如下:
//头⽂件
#include <QMetaType>
//⾃定义类型
struct AsynResults {
int imgId;
QImage image;
int result;
};
//在类构造时,注册类型
qRegisterMetaType<AsynResults>("AsynResults");
//QMetaObject::invokeMethod调⽤椰蓉
QMetaObject::invokeMethod(this, "reportImageResultAsync",
Q_ARG(AsynResults, asynresults);