
发件人:
作为基于C ++的跨平台GUI系统,Qt可以为用户提供构建图形用户界面的强大功能. 为了满足用户构建复杂的图形界面系统的需求,Qt提供了丰富的多线程编程支持.
作为基于C ++的跨平台GUI系统,Qt可以为用户提供构建图形用户界面的强大功能. 为了满足用户构建复杂的图形界面系统的需求,Qt提供了丰富的多线程编程支持. 从2.2版开始,Qt主要从以下三个方面支持多线程编程: 1.构造一些与平台无关的基本线程类; 2.提交用户定义事件的线程安全方式; 3.线程之间的各种同步机制,例如信号量和全局锁. 所有这些为用户提供了极大的便利. 但是,在某些情况下,使用计时器机制比使用Qt自己的多线程机制来实现所需的功能更为方便,同时还避免了不安全的现象. 本文不仅讨论了Qt中的多线程支持机制,而且重点介绍了使用计时器机制模拟多线程编程的方法.
1. 系统支持多线程编程
Qt的多线程支持在不同平台上有所不同. 当用户在Windows操作系统上安装Qt系统时,线程支持是编译器的一个选项. Qt的mkfiles子目录包含不同类型的编译器的编译文件,带有-mt后缀的文件是唯一支持多个Threaded的文件.
在Unix操作系统中,运行配置脚本文件时,通过添加-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中提供了线程安全的事件提交过程. 提交的事件被放入队列中,并且GUI主事件线程被唤醒并将事件发送到相应的对象. 此过程与常规窗口系统事件处理过程相同. 值得注意的是,在调用事件处理过程时,它是在主事件线程中而不是在调用QThread :: postEvent方法的线程中调用的. 例如,用户可以强制另一个线程从一个线程重绘指定区域:
QWidget * mywidget;
QThread :: postEvent(mywidgetlinux下的多线程编程,新的QPaintEvent(QRect(0,0,100,100)));
但是,仅一个线程类是不够的. 为了编写支持多线程的程序,两个不同的线程需要实现对共享数据的互斥访问,因此Qt还提供了QMutex类,一个线程正在访问当需要锁定关键数据时,其他线程无法锁定关键数据,直到上一个线程释放关键数据为止. 只有这样,才能对关键数据进行原子操作.
此外,在某些情况下需要一些机制来使等待线程唤醒. 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类是用户定义的事件类. 它的事件ID为346798,显然与系统定义的事件类型没有冲突.
class UserEvent: public QCustomEvent //用户定义的事件类
{
公开
:
UserEvent(QString s): QCustomEvent(346798),sz(s){; }
QString str()const {return sz; }
私人:
QString sz;
};
UserThread类是从QThread类继承的子类. 除了定义相关变量和线程控制函数外,最重要的是在此函数中定义线程启动函数UserThread :: run(). 创建一个用户定义的事件UserEvent,并使用QThread类的post事件函数将事件提交到相应的接收对象.
UserThread类: 公共QThread //用户定义的线程类
{
公开
:

UserThread(QObject * r,QMutex * m,QWaitCondition * c);
QObject *;
}
void UserThread :: run()//线程类启动函数,在此函数中创建用户定义的事件
{UserEvent * re =新的UserEvent(结果字符串);
QThread :: postEvent(receiver,re);
}
UserWidget类是用户定义的QWidget类的子类,用于接收自定义事件. 此类使用slotGo()函数创建新的线程recv(UserThread类). 当收到相应的自定义事件时(当id为346798时),请使用customEvent函数来处理该事件.
void UserWidget :: slotGo()//用户定义控件的成员函数
{mutex.lock();
如果(!recv)
recv = new UserThread(this,&mutex,&condition);
recv-> start();
mutex.unlock();
}
void UserWidget :: customEvent(QCustomEvent * e)//用户定义的事件处理功能

{if(e-> type()== 346798)
{
UserEvent * re =(UserEvent *)e;
newstring = re-> str();
}
}
在此示例中linux下的多线程编程,在UserWidget对象中创建了一个新线程UserThread. 用户可以使用此线程来执行一些定期处理(例如从底层接收消息等),并在满足特定条件后提交用户定义的内容. UserWidget对象收到事件后,可以根据需要进行相应的处理. 通常,UserWidget对象可以正常执行某些例行处理,而不受基础消息的影响.
4. 使用计时器机制实现多线程编程
为避免在Qt系统中由多线程编程引起的问题,还可以使用系统中提供的计时器机制来实现类似的功能. 计时器机制将并发事件序列化,从而简化了并发事件的处理,从而避免了线程安全问题.
在下面的示例中,有多个对象同时需要从底层接收消息(可以通过Socket,FIFO和其他进程间通信机制),并且消息是随机接收的,并且GUI主线程负责接收消息. 主线程在接收消息时初始化相应的对象以开始处理并同时返回,以便主线程始终可以更新接口显示并接收来自外界的消息以同时控制多个对象另一方面,每个对象处理完消息后,需要通知GUI主线程. 对于此问题,可以使用第3节中的用户定义事件的方法在主线程中安装事件过滤器,以捕获从各个对象发送的自定义事件,然后发送信号以调用主线程之一Slot函数.
此外,您还可以在Qt中使用计时器机制来实现类似的功能,而不必担心线程安全问题. 这是代码的相关部分:
在用户定义的Server类中创建并启动计时器,并使用connect函数将计时器超时与读取设备文件数据相关联:
服务器: :服务器(QWidget *父级): QWidget(父级)
{
readTimer =新的QTimer(this); //创建并启动计时器
connect(readTimer,SIGNAL(timeout()),this,SLOT(slotReadFile())); //每当计时器超时时调用函数slotReadFile读取文件

readTimer-> start(100);
}
slotReadFile函数负责在计时器超时时从文件中读取数据,然后重新启动计时器:
int Server :: slotReadFile()//消息读取和处理功能
{
readTimer-> stop(); //暂时停止计时器
ret =读取(file,buf); //读取文件
if(ret == NULL)
{readTimer-> start(100); //如果没有新消息,请重新启动计时器
return(-1);
}
其他
根据buf ...中的内容将消息分发到每个相应的对象以进行处理;
readTimer-> start(100); //重新启动计时器
}
在此程序中,使用类似轮询的方法来定期读取用户指定的设备文件,并根据读取数据的内容将信息发送到每个相应的对象. 用户可以在自己的GUI主线程中创建Server类,以帮助实现基础的消息接收过程,同时仍然能够处理诸如界面显示之类的问题. 在每个对象完成处理之后,通过重新启动计时器来继续定期读取底层设备文件的过程. 当然,该方法适用于以下情况: 每个对象对事件的处理时间很短,并且底层设备发送的消息的频率相对较慢. 在这种情况下,上述方法可以完全满足用户的需求,同时避免处理一些与线程并发相关的复杂问题.
当然,使用计时器机制来实现多线程编程在某些方面有一定的局限性. 如何实现多线程编程以及如何编写更高效的代码仍有待开发人员进一步研究和讨论.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-261602-1.html
从哪里开始