Linux系统下QT中的多线程编程
副标题#e#
Qt 作为一种基于 C++ 的跨平台 GUI 系统,可以或许提供应用户结构图形用户界面的强大成果。为了满意 用户结构巨大图形界面系统的需求,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 要领的线程中被挪用。好比用户可以从一个线程中迫使另一个线程重画指定 区域:
QWidget *mywidget;
QThread::postEvent(mywidget, new QPaintEvent(QRect (0,0,100,100)));
然而,只有一个线程类是不足的,为编写出支持多线程的措施,还需要实现两个差异的线程对共有数 据的互斥会见,因此 Qt 还提供了 QMutex 类,一个线程在会见临界数据时,需要加锁,此时其他线程是 无法对该临界数据同时加锁的,直到前一个线程释放该临界数据。通过这种方法才气实现对临界数据的原 子操纵。
除此之外,还需要一些机制使得处于期待状态的线程在特定环境下被叫醒。QWaitCondition 类就提供 了这种成果。当产生特定事件时,QWaitCondition 将叫醒期待该事件的所有线程可能叫醒任意一个被选 中的线程。
#p#副标题#e#
3、用户自界说事件在多线程编程中的应用
在 Qt 系统中,界说了许多种类的事件,如按时器事件、鼠标移动事件、键盘事件、窗口控件事件等 。凡是,事件都来自底层的窗口系统,Qt 的主事件轮回函数从系统的事件行列中获取这些事件,并将它 们转换为 QEvent,然后传给相应的 QObjects 工具。
除此之外,为了满意用户的需求,Qt 系统还提供了一个 QCustomEvent 类,用于用户自界说事件,这 些自界说事件可以操作 QThread::postEvent() 可能QApplication::postEvent() 被发给各类控件或其他 QObject 实例,而 QWidget 类的子类可以通过 QWidget::customEvent() 事件处理惩罚函数利便地吸收到这 些自界说的事件。需要留意的是:QCustomEvent 工具在建设时都带有一个范例标识 id 以界说事件范例 ,为了制止与 Qt 系统界说的事件范例斗嘴,该 id 值应该大于列举范例 QEvent::Type 中给出的 "User" 值。
在下面的例子中,显示了多线程编程中如何操浸染户自界说事件类。UserEvent类是用户自界说的事件 类,其事件标识为346798,显然不会与系统界说的事件范例斗嘴。
class UserEvent : public QCustomEvent //用户自界说的事件类
{
public:
UserEvent(QString s) : QCustomEvent(346798), sz(s) { ; }
QString str() const { return sz; }
private:
QString sz;
};
#p#分页标题#e#
UserThread类是由QThread类担任而来的子类,在该类中除了界说有关的变量和线程节制函数外,最主 要的是界说线程的启动函数UserThread::run(),在该函数中建设了一个用户自界说事件UserEvent,并利 用QThread类的postEvent函数提交该事件给相应的吸收工具。
class UserThread : public QThread //用户界说的线程类
{
public:
UserThread(QObject *r, QMutex *m, QWaitCondition *c);
QObject *receiver;
}
void UserThread::run() //线程类启动函数,在该函数中建设了 一个用户自界说事件
{UserEvent *re = new UserEvent(resultstring);
QThread::postEvent(receiver, re);
}
UserWidget类是用户界说的用于吸收自界说事件的QWidget类的子类,该类操作slotGo()函数建设了一 个新的线程recv(UserThread类),当收到相应的自界说事件(即id为346798)时,操作customEvent函 数对事件举办处理惩罚。
void UserWidget::slotGo() //用户界说控件的成员函数
{ mutex.lock();
if (! 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();
}
}
在这个例子中,UserWidget工具中建设了新的线程UserThread,用户可以操作这个线程实现一些周期 性的处理惩罚(如吸收底层发来的动静等),一旦满意特定条件就提交一个用户自界说的事件,当UserWidget 工具收到该事件时,可以按需求做出相应的处理惩罚,而一般环境下,UserWidget工具可以正常地执行某些例 行处理惩罚,而完全不受底层动静的影响。
4、操作按时器机制实现多线程编程
为了制止Qt系统中多线程编程带来的问题,还可以利用系统中提供的按时器机制来实现雷同的成果。 按时器机制将并发的事件串行化,简化了对并发事件的处理惩罚,从而制止了thread-safe方面问题的呈现。
在下面的例子中,同时有若干个工具需要吸收底层发来的动静(可以通过Socket、FIFO等历程间通信 机制),而动静是随机收到的,需要有一个GUI主线程专门认真吸收动静。当收到动静时主线程初始化相 应工具使之开始处理惩罚,同时返回,这样主线程就可以始终更新界面显示并吸收外界发来的动静,到达同时 对多个工具的节制;另一方面,各个工具在处理惩罚完动静后需要通知GUI主线程。对付这个问题,可以操作 第3节中的用户自界说事件的要领,在主线程中安装一个事件过滤器,来捕获从各个工具中发来的自界说 事件,然后发出信号挪用主线程中的一个槽函数。
别的,也可以操作Qt中的按时器机制实现雷同的成果,而又不必担忧Thread-safe问题。下面就是有关 的代码部门:
在用户界说的Server类中建设和启动了按时器,并操作connect函数将按时器超时与读取设备文件数据 相关联:
Server:: Server(QWidget *parent) : QWidget(parent)
{
readTimer = new QTimer(this); //建设并启动按时器
connect(readTimer, SIGNAL(timeout()),
this, SLOT (slotReadFile())); //每当按时器超时时挪用函数slotReadFile读取文件
readTimer- >start(100);
}
slotReadFile函数认真在按时器超时时,从文件中读取数据,然 后从头启动按时器:
int Server::slotReadFile() // 动静读取和处理惩罚函数
{
readTimer->stop(); //临时遏制按时器计时
ret = read(file, buf ); //读取文件
if(ret == NULL)
{ readTimer->start(100); //当没有新动静时 ,从头启动按时器
return(-1);
}
else
按照buf中的内容 将动静分发给各个相应的工具处理惩罚……;
readTimer->start(100); //从头启动按时器
}
#p#分页标题#e#
在该措施中,操作了雷同轮循的方法按时对用户指定的设备文件举办读取,按照读到的数据内容将信 息发送到各个相应的工具。用户可以在本身的GUI主线程中建设一个Server类,辅佐实现底层的动静吸收 进程,而自己仍然可以处理惩罚诸如界面显示的问题。当各个工具完成处理惩罚后,通过从头启动按时器继承举办 周期性读取底层设备文件的进程。虽然,这种要领适合于各工具对事件的处理惩罚时间较短,而底层设备发来 动静的频率又相对较慢的环境。