针对自定义信号与槽的调用,已有示例验证。定时器消息默认在主线程触发,若需在子线程中执行,应如何实现?本文以定时器为例,详细说明系统消息在子线程中触发的方法与实现过程。
1、 创建一个继承自QThread的类,声明一个QTimer定时器及相应槽函数,在构造函数中完成定时器的连接并启动。
2、 在主窗口中声明一个 QTimer 定时器及其对应的槽函数,同时定义一个线程变量。构造函数中完成定时器的绑定,并启动定时器与线程,实现定时任务与多线程协同运行。
3、 运行后可见,两个定时器的输出均在主线程中执行。
4、 修改代码,在run函数里开启定时任务
5、 在两个定时器函数中设置断点进行调试,发现仅主线程的定时器被触发,子线程未执行其对应的槽函数。
6、 查看VS输出窗口,仅显示主函数的定时器信息,并提示定时器无法从其他线程启动。
7、 查找该错误的原因可知:QObject具有可重入性,其多数非GUI的子类,如QTimer、QTcpSocket、QUdpSocket和QProcess等也具备可重入特性,因此可用于多线程环境。然而,这些类的设计初衷是在线程内部创建并使用,即对象应在某个单一线程中实例化并在该线程中调用其方法。若在一个线程中创建对象,却在另一个线程中调用其成员函数,则无法确保正常运行,可能导致未定义行为或程序异常,因此应避免跨线程直接操作此类对象。
8、 在本例中,CMySignal类的成员变量QTimer m_timer是在主线程中创建CMySignal对象时初始化的,但实际调用该定时器的是CMySignal的run函数,而run函数运行在另一个独立线程中,导致跨线程操作引发错误提示。
9、 在线程类中声明定时器指针,于run函数内创建定时器实例,连接对应槽函数并启动执行。
10、 运行结果显示仅主线程定时器输出,子线程刚启动即被提示退出。
11、 需在run函数中添加exec函数调用,以启动子线程的消息循环机制。
12、 再次运行后,子线程的定时器槽函数仍在主线程中执行。
13、 此处信号与槽的连接采用默认方式,在多线程环境下等同于使用队列连接。
14、 队列连接通过postEvent内部实现,不具备实时性,槽函数的执行始终位于其所属对象所在线程中,而该对象处于主线程,因此槽函数将在主线程中运行。
15、 创建一个类用于处理定时器超时,在子线程中实例化该类对象。
16、 重新检查运行结果,子线程的定时器已在该线程中启动。
