1、常用的信号槽关联类型
(1)Qt:: Connection
自动连接:默认的方式。信号发出的线程和糟的对象在一个线程的时候相当于:DirectConnection, 如果是在不同线程,则相当于QueuedConnection。
当信号发出后,对应的槽函数将马上被调用。emit语句后的代码将在全部槽函数运行完成后被运行。在这个线程内是顺序运行、同步的。可是与其他线程之间肯定是异步的了。
(2)Qt::DirectConnection(直连方式)(信号与槽函数关系类似于函数调用。同步运行)
直接连接:相当于直接调用槽函数,但是当信号发出的线程和槽的对象不再一个线程的时候,则槽函数是在发出的信号中执行的。
当信号发出后,对应的槽函数将马上被调用。emit语句后的代码将在全部槽函数运行完成后被运行。在这个线程内是顺序运行、同步的。
如果信号所在线程和槽函数所在线程不是一个线程,会强制把槽函数拉到和信号所在的一样的线程来执行,并且是同步执行,这时打印的槽函数线程和信号线程ID是一个ID,emit后面的内容需要等到槽函数执行完毕才执行。
(3)Qt::QueuedConnection(排队方式)(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信。异步运行)
当信号发出后。排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号。调用对应的槽函数。emit语句后的代码将在发出信号后马上被运行。无需等待槽函数运行完成。
(4)Qt::BlockingQueuedConnection (信号和槽必须在不同的线程中。否则就产生死锁)
阻塞连接:发出信号后,当前线程emit后的程序会阻塞,等待槽函数执行完毕后才继续执行,相当于是不同的线程能够同步起来运行。
这个特别要注意,如果线程没启动,那信号和槽依然在同一线程,会死锁,吃了好几次亏了,一定一定要启动线程!!!
(5)Qt::UniqueConnection
防止重复连接。如果当前信号和槽已经连接过了,就不再连接了。同步异步机制与默认方式一样。
容易错的案例1:
MLAlignmentWrapper类中有两个函数,SetModel和startAlignmentMV。
SetModel函数中有信号槽的连接,startAlignmentMV函数中有信号的触发,当触发该信号后会执行
MLAlignmentModel类中的startSpotFind函数功能。
startSpotFind函数中有部分代码如下:
connect(this, SIGNAL(updateSpotData(QList<SpotData>))), widget, SLOT(updateCameraSpotData(QList<SpotData>))), Qt::BlockingQueuedConnection) connect(this, SIGNAL(updateSpotTableData(QList<SpotData>))), widget, SLOT(updateDotsSpotData(QList<SpotData>))), Qt::BlockingQueuedConnection) emit updateSpotData(m_SpotDataList); emit updateSpotTableData(m_SpotTableDataList);
MLAlignmentWrapper类已经被MoveToThread,假设这个线程已被启动并执行线程的startALignmentMV方法。emit信号驱动执行m_AlignmentModel的startSpotFind方法,如果MLAlignmentModel没有move到一个线程,那它就属于主线程,this和Widget同属于一个线程,那connect后有Block连接会死锁,如果move到了一个线程,那emit startMVSignal后m_AlignmentModel就开启了一个线程,内部有Block连接可以走通。如果把startALignmentMV函数中的emit注释,把上面的注释打开,现在startALignmentMV是一个线程的方法在执行m_AlignmentModel->startSpotFind,不管MLAlignmentModel有没有move到一个线程,startSpotFind都属于MLAlignmentWrapper线程,因为我直接调用的是AlignmentModel方法没有启动线程,在startSpotFind内部打印线程ID是和MLAlignmentWrapper线程ID一样的,那startSpotFind中的this属于MLAlignmentWrapper线程,Widget属于主线程,不同线程自然Block连接方式可以走通。
现在做个修改:SetModel里的connect加个Block连接方式,不把MLAlignmentWrapper类move到一个线程,那么MLAlignmentWrapper就属于主线程,MLAlignmentModel类move到一个线程,在emit startMVSignal后就启动了MLAlignmentModel线程,并执行connect内容,因为this和m_AlignmentWidget属于不同的线程,想要同步执行,后面得要加Block连接,这时候主线程需要等待startSpotFind执行完才继续走,而内部connect连接的this属于MLAlignmentModel线程,alignmentWidget属于主线程,想要同步执行后面得要加Block连接,这样的话就有个问题,前面是主线程需要等MLAlignmentModel线程执行完才走,后面是MLAlignmentModel线程需要等主线程执行完才走,形成死循环。
易错的案例2:
回调函数在线程中循环被调用,connect在主线程被连接,这样的话emit属于子线程触发,在主线程执行showImage(),后面加block属于不同线程同步执行,emit之后需要等主线程showImage走完才接着走子线程的qDebug打印。如果我在主线程执行了子线程的join()功能,那么会阻塞主线程直到子线程结束,这样的话,子线程需要等主线程走完才走,而主线程被阻塞需要等子线程走完,形成死循环。