Qt多线程
Qt作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功能。为了满足用户构造复杂图形界面系统的需求,Qt提供了丰富的多线程编程支持。从 2.2 版本开始,Qt主要从下面三个方面对多线程编程提供支持:一、构造了一些基本的与平台无关的线程类;二、提交用户自定义事件的 Thread-safe方式;三、多种线程间同步机制,如信号量,全局锁。这些都给用户提供了极大的方便。不过,在某些情况下,使用定时器机制能够比利用 Qt本身的多线程机制更方便地实现所需要的功能,同时也避免了不安全的现象发生。本文不仅对 Qt中的多线程支持机制进行了讨论,还着重探讨了利用定时器机制模拟多线程编程的方法。
1、系统对多线程编程的支持
不同的平台对 Qt 的多线程支持方式是不同的。当用户在 Windows 操作系统上安装 Qt 系统时,线程支持是编译器的一个选项,在 Qt 的 mkfiles 子目录中包括了不同种类编译器的编译文件,其中带有 -mt 后缀的文件才是支持多线程的。
而在 Unix 操作系统中,线程的支持是通过在运行 configure 脚本文件时添加 -thread选项加入的。安装过程将创建一个独立的库,即libqt-mt,因此要支持多线程编程时,必须与该库链接(链接选项为-lqt-mt),而不是与通常的 Qt 库(-lqt)链接。
另外,无论是何种平台,在增加线程支持时都需要定义宏QT_THREAD_SUPPORT(即增加编译选项-DQT_THREAD_SUPPORT)。在 Windows 操作系统中,这一点通常是在qconfig.h 文件中增加一个选项来实现的。而在 Unix 系统中通常添加在有关的 Makefile 文件中。
2、Qt中的线程类
在Qt 系统中与线程相关的最重要的类当然是 QThread 类,该类提供了创建一个新线程以及控制线程运行的各种方法。线程是通过QThread::run() 重载函数开始执行的,这一点很象 Java 语言中的线程类。在 Qt 系统中,始终运行着一个GUI主事件线程,这个主线程从窗口系统中获取事件,并将它们分发到各个组件去处理。在 QThread类中还有一种从非主事件线程中将事件提交给一个对象的方法,也就是 QThread::postEvent()方法,该方法提供了 Qt 中的一种Thread-safe 的事件提交过程。提交的事件被放进一个队列中,然后 GUI主事件线程被唤醒并将此事件发给相应的对象,这个过程与一般的窗口系统事件处理过程是一样的。值得注意的是,当事件处理过程被调用时,是在主事件线程中被调用的,而不是在调用QThread::postEvent 方法的线程中被调用。比如用户可以从一个线程中迫使另一个线程重画指定区域:
|
除此之外,还需要一些机制使得处于等待状态的线程在特定情况下被唤醒。QWaitCondition 类就提供了这种功能。当发生特定事件时,QWaitCondition 将唤醒等待该事件的所有线程或者唤醒任意一个被选中的线程。
3、用户自定义事件在多线程编程中的应用
在 Qt 系统中,定义了很多种类的事件,如定时器事件、鼠标移动事件、键盘事件、窗口控件事件等。通常,事件都来自底层的窗口系统,Qt 的主事件循环函数从系统的事件队列中获取这些事件,并将它们转换为 QEvent,然后传给相应的 QObjects 对象。
除此之外,为了满足用户的需求,Qt 系统还提供了一个 QCustomEvent 类,用于用户自定义事件,这些自定义事件可以利用QThread::postEvent() 或者QApplication::postEvent() 被发给各种控件或其他 QObject实例,而 QWidget 类的子类可以通过 QWidget::customEvent()事件处理函数方便地接收到这些自定义的事件。需要注意的是:QCustomEvent 对象在创建时都带有一个类型标识 id以定义事件类型,为了避免与 Qt 系统定义的事件类型冲突,该 id 值应该大于枚举类型 QEvent::Type 中给出的 "User" 值。
在下面的例子中,显示了多线程编程中如何利用用户自定义事件类。
UserEvent类是用户自定义的事件类,其事件标识为346798,显然不会与系统定义的事件类型冲突。
|
|
|
4、利用定时器机制实现多线程编程
为了避免Qt系统中多线程编程带来的问题,还可以使用系统中提供的定时器机制来实现类似的功能。定时器机制将并发的事件串行化,简化了对并发事件的处理,从而避免了thread-safe方面问题的出现。
在下面的例子中,同时有若干个对象需要接收底层发来的消息(可以通过Socket、FIFO等进程间通信机制),而消息是随机收到的,需要有一个GUI主线程专门负责接收消息。当收到消息时主线程初始化相应对象使之开始处理,同时返回,这样主线程就可以始终更新界面显示并接收外界发来的消息,达到同时对多个对象的控制;另一方面,各个对象在处理完消息后需要通知GUI主线程。对于这个问题,可以利用第3节中的用户自定义事件的方法,在主线程中安装一个事件过滤器,来捕捉从各个对象中发来的自定义事件,然后发出信号调用主线程中的一个槽函数。
另外,也可以利用Qt中的定时器机制实现类似的功能,而又不必担心Thread-safe问题。下面就是有关的代码部分:
在用户定义的Server类中创建和启动了定时器,并利用connect函数将定时器超时与读取设备文件数据相关联:
|
|
当然,利用定时器机制实现多线程编程在某些方面具有一定的局限性,有关到底如何实现多线程编程,如何编写出效率更高的代码,还有待于开发者进一步研究和探讨。
参考资料
(1)Qt官方文档 http://doc.trolltech.com/3.2/index.html
(2)Qt源代码:QThread, QCustomEvent,QTimer.
(3)Advanced Programming in the UNIX Environment, W. Richard Stevens.
【Qt多线程】续欣 2004 年 4 月www-128.ibm.com/developerworks
推荐阅读
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- Linux下面如何查看tomcat已经使用多少线程
- 说得清,说不清