情况一
QTimer* timer = new QTimer;
timer->start(1000);
int t = 0;
QObject::connect(timer, &QTimer::timeout, this, [=]() {
QEventLoop loop;
qDebug() << "chufa";
QTimer::singleShot(3000, this, [&]() {
qDebug() << "quit";
loop.quit();
});
loop.exec();
qDebug() << u8"执行完成";
});
测试结果
由于timer属于主线程,此时连接的类型为Qt::DirectConnection
与其连接的槽函数会立即调用,应该是回调函数的形式,执行线程为信号发送的线程
此时,信号会等待槽函数执行完成后再发送一次信号。这并不是线程被阻塞,而是由于DirectConnection连接方式导致直接调用槽函数,
槽函数却需要等待loop.quit()后才执行完成,即这个信号才会执行完成。然后才会继续触发信号。期间timer不会计时。
情况二
QTimer* timer = new QTimer;
timer->start(1000);
int t = 0;
QObject::connect(timer, &QTimer::timeout, this, [=]() {
QEventLoop loop;
qDebug() << "chufa";
QTimer::singleShot(3000, this, [&]() {
qDebug() << "quit";
loop.quit();
},Qt::QueuedConnection);
loop.exec();
qDebug() << u8"执行完成";
});
仅是改变信号槽的连接方式为QueuedConnection
此时只要信号触发就会执行代码到loop.exec()处,因为QueuedConnection连接方式为队列
但是需要注意的是
即使某一次触发调用了loop.quit(),但是只要还存在其他的子事件循环,则后面的代码不会执行。并且由于这个时间差的关系,还会导致报错堆栈溢出的错误。从而导致程序挂掉。
如果timer对象在子线程,结果和上面一致
因此此种方式不可取。
情况三
QTimer* timer = new QTimer;
timer->start(1000);
QThread* thread = new QThread();
timer->moveToThread(thread);
thread->moveToThread(thread);
connect(thread, &QThread::started, timer, QOverload<>::of(&QTimer::start));
thread->start();
int t = 0;
QObject::connect(timer, &QTimer::timeout, this, [=]() {
if (loop.isRunning()) return;
qDebug() << "chufa";
QTimer::singleShot(3000, this, [&]() {
qDebug() << "quit";
loop.quit();
});
loop.exec();
qDebug() << u8"执行完成";
});
timer处于子线程,槽函数处于线程,连接方式为QueuedConnection
loop为成员变量 QEventLoop loop
此时,即使信号多次触发,但是只要上一次的isRunning为true,则会自动丢弃该次触发。从而保证运行安全