QTimer::singleShot:不是“延时函数”,而是事件循环的时间接口
你有没有试过在 Qt 中写这样一段代码:
void Widget::onButtonClicked() { // 想让按钮点击后停顿一下再变灰,防止误点 QThread::msleep(300); // ❌ 危险! ui->button->setEnabled(false); }运行起来——界面瞬间卡死,鼠标悬停无反馈,窗口标题栏变灰,任务管理器里 CPU 占用却很低。这不是性能问题,是你亲手掐断了 Qt 的呼吸。
Qt 不是靠“轮询”或“等待”活着的,它靠的是事件循环(QEventLoop)持续泵血。而QTimer::singleShot,就是 Qt 给你的一根精准、安全、无需换气的“时间导管”——它不暂停任何东西,只是悄悄在事件队列里插了一张小纸条:“300 毫秒后,请执行这个函数”。
它到底做了什么?拆开看
很多人以为singleShot是“创建了一个临时 QTimer”,其实完全相反:它根本没创建任何 QObject 实例。你传进去的this、&MyClass::slot或一个 lambda,Qt 只是把它们打包成一个轻量级的内部定时器句柄(timerId),登记进当前线程的QAbstractEventDispatcher。这个调度器底层在 Windows 上用CreateTimerQueueTimer,Linux 上用timerfd_create + epoll,macOS 上用dispatch_source_t—— 全部是系统级异步机制,零忙等、零线程挂起。
关键在于:这张“小纸条”不会立刻执行,它必须排队,等事件循环下一次processEvents()轮到它。
所以这行代码:
QTimer::singleShot(500, this, &Widget::loadData);实际发生的是:
- Qt 记下:“500ms 后,给 this 对象发一个 TimerEvent”
- 500ms 到了 → 系统通知事件分发器 → 分发器把
QTimerEvent塞进当前线程的