项目中遇到了关于多线程编程问题
Class obj中有一个QTimer。线程A和线程B都有机会重启这个QTimer。
然而,在QT文档中QTimer并不是一个线程安全的类,因此就出现了怎样保证跨线程调用的安全性
首先,obj是是有线程概念的,它必须属于一个特定线程的(当然也可以使用moveToThread将它放入另外一个线程中,所有继承自QObject都有一个threadid的属性,用来标明此对象所属线程)。
为了能够跨线程调用,一种方法是使用类似线程锁进行保护,另外一种方法使用QT的信号槽机制。
信号槽机制网上的内容很多,尤其是对于QObject::connect函数的第五个参数的说明。这个参数可以有好几个选项,最终的结果是发出的信号应该到底是直接处理(DirectConnection)还是转变成消息进入QEventLoop中进行处理。
当直接处理时,emit 信号等同于直接调用槽函数(例如使用DirectConnection或者使用AutoConnection时,发送者和接收者在同一个线程中。使用DirectConnection需要注意,因为这会导致跨线程调用问题)。
不直接处理时,emit 信号会生成一个QEvent::MetaCall的event,将此事件放入到槽函数所属对象所在的线程中的QEventLoop中,这样,线程就有机会从中取到MetaCall事件,然后通过QObject::event实现事件的解释,最终定位到槽函数中。
参考文章:http://woboq.com/blog/how-qt-signals-slots-work.html
class TestObject : public QObject
{
Q_OBJECT
public:
explicit TestObject(QObject *parent = 0):QObject(parent),mTimer(this){
connect(&mTimer,SIGNAL(timeout()),this,SLOT(Timeout()));
mTimer.start(1000);
}
public slots:
void Timeout(){
printf("timeout\n");
}
private:
QTimer mTimer;
};
直接构造后,每1s输出1个打印。
信号的连接:
connect(&mTimer,SIGNAL(timeout()),this,SLOT(Timeout()));
SIGNAL和SLOT都是宏定义,在qobjectdefs.h文件中定义,如下:
#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QTOSTRING(__LINE__)
# ifndef QT_NO_KEYWORDS
# define METHOD(a) qFlagLocation("0"#a QLOCATION)
# endif
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
# define METHOD(a) "0"#a
# endif
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
#endif
上述调试版本增加了文件名和所在行信息
非调试版本只是添加了"1"或者"2"的前缀信息。
connect(&Timer,SIGNAL(timeout()),this,SLOT(Timeout()))最后被扩展为:
connect(&Timer,"1timeout()",this,"2Timeout()",Qt::AutoConnection) ,这个函数的定义如下,注释在代码中:
bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) //connect callback, 没有看到哪个地方在用,先略过
return true;
}
#ifndef QT_NO_DEBUG
bool warnCompat = true;
#endif
if (type == Qt::AutoCompatConnection) {
type = Qt::AutoConnection;
#ifndef QT_NO_DEBUG
warnCompat = false;
#endif
}
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return false;
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind")) //判断信号是否1开头
return false;
const QMetaObject *smeta = sender->metaObject();//Meta Object 系统
const char *signal_arg = signal;
++signal; //skip code
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); //从Meta Object系统中获取signal信息,局部索引
if (signal_index < 0) {
//没找到,试一试将发送者的信号参数中的空白行去掉,将const放在最前面,将const引用类型换成值类型。(当然肯定不是这么简单,例如char* const *就不能变成const char**)
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
}
if (signal_index < 0) {
// re-use tmp_signal_name and signal from above
//还没找到,试一试将MetaObject中存储的信号参数中的空白行去掉,将const放在最前面,将const引用类型换成值类型。
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
}
//例如:信号声明为 void MySignal(QString)。 对应的Signature为: "2MySignal(QString)"
//连接时: connect(this,SIGNAL(MySignal(const QString&))....)。对应的Signature为: "2MySignal(const QString&)", 字符串肯定不匹配。不过Normalize后得到"1MySignal(QString)"就能匹配了。
if (signal_index < 0) {//没有找到signal
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
int signalOffset, methodOffset;
computeOffsets(smeta, &signalOffset, &methodOffset);
int signal_absolute_index = signal_index + methodOffset;
signal_index += signalOffset;
QByteArray tmp_method_name;
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))//检查接收者(可能是slot,也可能是signal)
return false;
const char *method_arg = method;
++method; // skip code
const QMetaObject *rmeta = receiver->metaObject();
int method_index_relative = -1;
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
break;
}
if (method_index_relative < 0) {
//和信号一样,可能会对参数做一些修改,然后看看能不能找到Method
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
// rmeta may have been modified above
rmeta = receiver->metaObject();
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
if (method_index_relative < 0)
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
if (method_index_relative < 0)
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
break;
}
}
if (method_index_relative < 0) { //还没找到Receive的Method,只能报错了。
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
if (!QMetaObject::checkConnectArgs(signal, method)) { //检查信号和槽函数的参数是否匹配:参数一致,或者接收者参数多于发送者参数(接收者参数有默认值)
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return false;
}
int *types = 0;
//跨线程发送信号,参数类型必须能够被QtMetaObject识别(若不能识别,最简单的方法是发送这个类型的指针)
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
return false;
#ifndef QT_NO_DEBUG
if (warnCompat) {
QMetaMethod smethod = smeta->method(signal_absolute_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
}
#endif
//正真的注册发生在这个函数中
//事实上,发送者这个QObject拥有一个QObjectConnectionListVector类型的列表,其中每项的数据为:QObjectPrivate::Connection类型的链表。
//这是一个链表数组
if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
return false;
const_cast<QObject*>(sender)->connectNotify(signal - 1);
return true;
}
前面只是做了检查工作,关键的工作发生在QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)。
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
int method_offset = rmeta ? rmeta->methodOffset() : 0;
QObjectPrivate::StaticMetaCallFunction callFunction =
(rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection) {
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
if (connectionLists && connectionLists->count() > signal_index) { //已经注册信号了,增加新的接收者(支持1个信号,多个接收者)
const QObjectPrivate::Connection *c2 =
(*connectionLists)[signal_index].first;
int method_index_absolute = method_index + method_offset;
while (c2) {
if (c2->receiver == receiver && c2->method() == method_index_absolute) //重复注册
return false;
c2 = c2->nextConnectionList;
}
}
type &= Qt::UniqueConnection - 1;
}
QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
c->sender = s;
c->receiver = r;
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->argumentTypes = types;
c->nextConnectionList = 0;
c->callFunction = callFunction;
QT_TRY {
QObjectPrivate::get(s)->addConnection(signal_index, c); //添加到向量表中。
} QT_CATCH(...) {
delete c;
QT_RETHROW;
}
c->prev = &(QObjectPrivate::get(r)->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
QObjectPrivate *const sender_d = QObjectPrivate::get(s);
if (signal_index < 0) {
sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
} else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
}
return true;
}
信号的发送:
例如:emit test2("123")实际上是 test2("123")
Mysignal的定义在Moc文件中可以看到。
void TestObject::test2(QString _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
上面可以看到,参数个数包括返回值(返回值用0表示)。
QString被转换成了void*类型了。若是发送者和接收者在同一个线程里,这样不会有问题。 若是不在同一个线程里,接收者拿到的指针会不会失效呢?(在后面可以看到,实际上,跨线程发送信号会导致参数备份)
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
int signalOffset;
int methodOffset;
computeOffsets(m, &signalOffset, &methodOffset); //老规矩,先得到索引位置
int signal_index = signalOffset + local_signal_index;
//不好意思,这个信号没有人connect
if (!sender->d_func()->isSignalConnected(signal_index))
return; // nothing connected to these signals, and no spy
//不好意思,这个Object被设置成阻塞(不能发送任何信号了)
//bool QObject::blockSignals ( bool block )
if (sender->d_func()->blockSig)
return;
int signal_absolute_index = methodOffset + local_signal_index;
void *empty_argv[] = { 0 };
if (qt_signal_spy_callback_set.signal_begin_callback != 0) { //QTest框架用的东西,略过(QSignalDumper)
qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,
argv ? argv : empty_argv);
}
Qt::HANDLE currentThreadId = QThread::currentThreadId();
QMutexLocker locker(signalSlotLock(sender));
//发送者的连接向量
QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
if (!connectionLists) {
locker.unlock();
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
return;
}
//标记这个连接向量表正在使用
++connectionLists->inUse;
//得到向量表中的链表
const QObjectPrivate::ConnectionList *list;
if (signal_index < connectionLists->count())
list = &connectionLists->at(signal_index);
else
list = &connectionLists->allsignals;
do {
//遍历链表
QObjectPrivate::Connection *c = list->first;
if (!c) continue;
// We need to check against last here to ensure that signals added
// during the signal emission are not emitted in this emission.
QObjectPrivate::Connection *last = list->last;
do {
if (!c->receiver) //接收者为空
continue;
QObject * const receiver = c->receiver;
const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) { //异步,直接使用事件来处理吧
//queue_activate需要看仔细咯
queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
continue;
#ifndef QT_NO_THREAD
} else if (c->connectionType == Qt::BlockingQueuedConnection) {//BlockingQueuedConnection需要单独处理
locker.unlock();
if (receiverInSameThread) { //发送者和接收者在一个线程里,会让自己阻塞,需要抛出警告信息
qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
"Sender is %s(%p), receiver is %s(%p)",
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
}
QSemaphore semaphore;
QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
c->callFunction,
sender, signal_absolute_index,
0, 0,
argv ? argv : empty_argv,
&semaphore));
semaphore.acquire(); //阻塞自己,等待接收者调用semaphore.release
locker.relock();
continue;
#endif
}
//下面是处理同一线程情况
QObjectPrivate::Sender currentSender;
QObjectPrivate::Sender *previousSender = 0;
/* Sender数据结构
struct Sender
{
QObject *sender;
int signal;
int ref;
};
*/
if (receiverInSameThread) {
currentSender.sender = sender;
currentSender.signal = signal_absolute_index;
currentSender.ref = 1;
previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);
}
//QMetaObject的callFunction
//typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
//寻找接收者Method的索引位置
const int method_relative = c->method_relative;
/*
moc文件中MetaObject信息如下:
const QMetaObject TestObject::staticMetaObject = {
{ &QObject::staticMetaObject, qt_meta_stringdata_TestObject,
qt_meta_data_TestObject, &staticMetaObjectExtraData }
};
通过metaObject接口那大QMetaObject数据,其中methodOffset()得到method索引(从父类算起的绝对索引)
const QMetaObject *TestObject::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
}
*/
if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
//we compare the vtable to make sure we are not in the destructor of the object.
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0)
qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);
/*
callFunction是一个函数指针,定义在我们的moc文件中
void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
TestObject *_t = static_cast<TestObject *>(_o);
switch (_id) {
case 0: _t->test2(); break;
case 1: _t->Timeout(); break;
case 2: _t->testSlot(); break;
default: ;
}
}
Q_UNUSED(_a);
}
*/
callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());
locker.relock();
} else {
//什么情况下会进入这个分支??
//
const int method = method_relative + c->method_offset;
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
qt_signal_spy_callback_set.slot_begin_callback(receiver,
method,
argv ? argv : empty_argv);
}
#if defined(QT_NO_EXCEPTIONS)
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
QT_TRY {
/*
int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{
if (QMetaObject *mo = object->d_ptr->metaObject)
return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
else
return object->qt_metacall(cl, idx, argv);
}
//qt_metacall也在moc文件中定义
*/
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
} QT_CATCH(...) {
locker.relock();
if (receiverInSameThread)
QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);
--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned && !connectionLists->inUse)
delete connectionLists;
QT_RETHROW;
}
#endif
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, method);
locker.relock();
}
//
if (receiverInSameThread)
QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);
if (connectionLists->orphaned)
break;
} while (c != last && (c = c->nextConnectionList) != 0);
if (connectionLists->orphaned)
break;
} while (list != &connectionLists->allsignals &&
//start over for all signals;
((list = &connectionLists->allsignals), true));
--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
//对向量表做一些维护工作
if (connectionLists->orphaned) {
if (!connectionLists->inUse)
delete connectionLists;
} else if (connectionLists->dirty) {
sender->d_func()->cleanConnectionLists();
}
locker.unlock();
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
}
其中的queued_activate函数定义:
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
QMetaMethod m = sender->metaObject()->method(signal);
int *tmp = queuedConnectionTypes(m.parameterTypes());
if (!tmp) // cannot queue arguments
tmp = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
if (tmp != &DIRECT_CONNECTION_ONLY)
delete [] tmp;
}
}
//参数有问题。。
if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
return;
//得到参数个数
int nargs = 1; // include return type
while (c->argumentTypes[nargs-1])
++nargs;
int *types = (int *) qMalloc(nargs*sizeof(int));
Q_CHECK_PTR(types);
void **args = (void **) qMalloc(nargs*sizeof(void *));
Q_CHECK_PTR(args);
types[0] = 0; // return type
args[0] = 0; // return value
//在这里,将信号中的参数类型和数据存储到args和types中,可以认为是做了一个备份。。
/*
void * QMetaType::construct ( int type, const void * copy = 0 ) [static]
Returns a copy of copy, assuming it is of type type. If copy is zero, creates a default type.
type的类型包括常用的int,float等基本类型,也包括QDate,QDateTime,QString,QByteArray等QT类型,也可以是用户注册的一些类型。
不管如何,都会将信号传递过来的参数进行拷贝。例如:当信号中的参数为QByteArray时:args[n]=new NS(QByteArray)(*static_cast<const NS(QByteArray)*>(copy));
*/
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
//发送QMetaCallEvent
QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
c->method_relative,
c->callFunction,
sender, signal, nargs,
types, args));
}
注意其中的QMetaCallEvent。
QTimer触发事件过程
int QObject::startTimer(int interval)
{
Q_D(QObject);
if (interval < 0) {
qWarning("QObject::startTimer: QTimer cannot have a negative interval");
return 0;
}
d->pendTimer = true; // set timer flag
if (!d->threadData->eventDispatcher) {
qWarning("QObject::startTimer: QTimer can only be used with threads started with QThread");
return 0;
}
return d->threadData->eventDispatcher->registerTimer(interval, this);
}
void QEventDispatcherUNIX::registerTimer(int timerId, int interval, QObject *obj)
{
#ifndef QT_NO_DEBUG
if (timerId < 1 || interval < 0 || !obj) {
qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
return;
} else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
qWarning("QObject::startTimer: timers cannot be started from another thread");
return;
}
#endif
Q_D(QEventDispatcherUNIX);
d->timerList.registerTimer(timerId, interval, obj);
}
也就是说,这个定时器被保存到timerList中了。而timerList在QEventDispatcher中管理,而这个时间分发器又是QThreadData的成员。QThreadData代表着一个线程。
发过来说,一个线程中所有的QObject拥有同一份QThreadData,通过这个成员可以得到QEventDispatcher。每一个线程中其实都有至少一个Event Queue的,QeventDispatcher实现对event queue的管理
定时事件如何判定
bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherUNIX);
d->interrupt = false;
// we are awake, broadcast it
emit awake();
//立刻分发所有posted事件
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
int nevents = 0;
const bool canWait = (d->threadData->canWait
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait)
emit aboutToBlock();
if (!d->interrupt) {
// return the maximum time we can wait for an event.
timeval *tm = 0;
timeval wait_tm = { 0l, 0l };
if (!(flags & QEventLoop::X11ExcludeTimers)) {
if (d->timerList.timerWait(wait_tm))
tm = &wait_tm;
}
if (!canWait) {
if (!tm)
tm = &wait_tm;
// no time to wait
tm->tv_sec = 0l;
tm->tv_usec = 0l;
}
nevents = d->doSelect(flags, tm); //处理QSocketNotifier事件
// activate timers
if (! (flags & QEventLoop::X11ExcludeTimers)) {
nevents += activateTimers(); //处理定时器
}
}
// return true if we handled events, false otherwise
return (nevents > 0);
}
int QTimerInfoList::activateTimers()
{
if (qt_disable_lowpriority_timers || isEmpty())
return 0; // nothing to do
int n_act = 0, maxCount = 0;
firstTimerInfo = 0;
//当前时间
timeval currentTime = updateCurrentTime();
//
repairTimersIfNeeded();
//QTimerInfoList是有序的
// Find out how many timer have expired
for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
if (currentTime < (*it)->timeout)
break;
maxCount++;
}
//fire the timers.
while (maxCount--) {
if (isEmpty())
break;
QTimerInfo *currentTimerInfo = first();
if (currentTime < currentTimerInfo->timeout)
break; // no timer has expired
if (!firstTimerInfo) {
firstTimerInfo = currentTimerInfo;
} else if (firstTimerInfo == currentTimerInfo) {
// avoid sending the same timer multiple times
break;
} else if (currentTimerInfo->interval < firstTimerInfo->interval
|| currentTimerInfo->interval == firstTimerInfo->interval) {
firstTimerInfo = currentTimerInfo;
}
// remove from list
removeFirst();
// determine next timeout time
currentTimerInfo->timeout += currentTimerInfo->interval;
if (currentTimerInfo->timeout < currentTime)
currentTimerInfo->timeout = currentTime + currentTimerInfo->interval;
// reinsert timer
timerInsert(currentTimerInfo);
if (currentTimerInfo->interval.tv_usec > 0 || currentTimerInfo->interval.tv_sec > 0)
n_act++;
if (!currentTimerInfo->activateRef) {
// send event, but don't allow it to recurse
currentTimerInfo->activateRef = ¤tTimerInfo;
//这里生成QTimerEvent,发送给QTimer,在QTimer::timerEvent中进行处理
QTimerEvent e(currentTimerInfo->id);
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
if (currentTimerInfo)
currentTimerInfo->activateRef = 0;
}
}
firstTimerInfo = 0;
return n_act;
}
QEventDispatcher会处理event queue中时间(好像只看到QSocketNotifier和QTimer),
需要注意:发送事件使用的是sendEvent,这意味着,当前线程会直接调用currentTimerinfo->obj对应的定时器事件的处理函数。为了保证一致性,需要currentTimerInfo->obj关联的是当前线程。
当timeout时,生成的QTimerEvent会调用到QTimer::timerEvent接口。
定时器响应:
void QTimer::timerEvent(QTimerEvent *e)
{
if (e->timerId() == id) {
if (single) //single shot定时器
stop();
emit timeout();
}
}
看到emit timeout(),又回到了信号与槽。
再看看QMetaCallEvent,它其实是一个类型为QEvent::MetaCall的事件。
跨线程信号槽调用时,拥有槽函数(信号)的对象所在的线程在处理事件时,发现了QMetaCallEvent,通过解析,能够从里面拿到接收者信息,以及其他接收者槽函数或者信号,再通过MetaObject的callFunction实现调用
QTimer::SingleShoot
另外我的有问题的代码如下:
class Object:public QObject{
Q_OBJECT
public:
Object(){
mTimer.setSingleShot(true);
connect(&mTimer,SIGNAL(timeout()),this,SLOT(timeout()));
mTimer.start(1000);
}
private slots:
void timeout(){
mTimer.start(1000);
}
private:
QTimer mTimer;
}
Object *A=new Object();
moveToThread(new QThread());
A关联到线程A中,不过mTimer还保留在原来的线程中(假设为B)
mTimer第一次start的时候,mTimer被注册到线程B的eventDispatcher中。当timeout发生时,mTimer第二次调用start的时候,mTimer关联线程B,却在线程A的槽函数中被访问。并且在start的时候会访问线程B的QEventDispatcher。这些核心数据有些并不是线程安全的(不能跨线程访问),从而会导致一些问题。。