连接不上的问题有很多种,如信号与槽的参数不匹配,参数为自定义类型等等。今天碰到的一个问题是多线程中,信号与槽一直连接不上。防止忘记,记录一下。
这个问题的情景是一个QObject的派生类不是在主线程里面创建的,然后这个派生类用默认的连接方式去连接信号,这时是连接不上的。
问题的根源就在于,异步连接的时候,这个信号是由接收者所在的线程的事件处理机制来处理的,如果接收者所在的线程没有事件处理的话,这个信号就不会被处理。看看代码:
voidQCoreApplication::postEvent(QObject*receiver,QEvent*event, int priority)
{
if (receiver == 0) {
qWarning("QCoreApplication::postEvent: Unexpected null receiver");
delete event;
return;
}
QThreadData* volatile *pdata= &receiver->d_func()->threadData;
QThreadData*data = *pdata;
if (!data) {
// postingduring destruction? just delete the event to prevent a leak
delete event;
return;
}
// lock the postevent mutex
data->postEventList.mutex.lock();
// if object hasmoved to another thread, follow it
while (data != *pdata) {
data->postEventList.mutex.unlock();
data =*pdata;
if (!data) {
//posting during destruction? just delete the event to prevent a leak
deleteevent;
return;
}
data->postEventList.mutex.lock();
}
QMutexUnlockerlocker(&data->postEventList.mutex);
// if this is oneof the compressible events, do compression
if (receiver->d_func()->postedEvents
&& self&& self->compressEvent(event,receiver,&data->postEventList)){
return;
}
if (event->type() ==QEvent::DeferredDelete&&data == QThreadData::current()) {
// rememberthe current running eventloop for DeferredDelete
// eventsposted in the receiver's thread
static_cast<QDeferredDeleteEvent *>(event)->level =data->loopLevel;
}
// delete theevent on exceptions to protect against memory leaks till the event is
// properly ownedin the postEventList
QScopedPointer<QEvent>eventDeleter(event);
data->postEventList.addEvent(QPostEvent(receiver,event, priority));
eventDeleter.take();
event->posted =true;
++receiver->d_func()->postedEvents;
data->canWait =false;
locker.unlock();
QAbstractEventDispatcher*dispatcher =data->eventDispatcher.loadAcquire();
if (dispatcher)
dispatcher->wakeUp();
}
从上面可以看出,信号被包装成一个事件加到了接收者所在的线程的事件队列当中去了。
暂时想到的几种解决方法:
-
在接收者创建线程中,把接收者移动到主线程中:
pReceiverObj->moveToThread(QApplication::instance()->thread());
这样发送信号的时候,就会在主线程事件队列处理中来处理了。 -
把connect的最后一个参数改成Qt::DirectConnection,使用直连接的方式来连接信号与槽。
-
启动QThread自己的事件队列处理(exec())。
如果有什么错误和问题,请各位多多指教。