Qt中的多线程编程

2010-08-18  来源:本站原创  分类:编程coding  人气:5 

Qt中的多线程编程

级 别: 初级

续欣 ([email protected]),

2004 年 4 月 01 日

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 将唤醒等待该事件的所有线程或者唤醒任意一个被选中的线程。


回页首

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;    }; 

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);    //重新启动定时器} 

在该程序中,利用了类似轮循的方式定时对用户指定的设备文件进行读取,根据读到的数据内容将信息发送到各个相应的对象。用户可以在自己 的GUI主线程中创建一个Server类,帮助实现底层的消息接收过程,而本身仍然可以处理诸如界面显示的问题。当各个对象完成处理后,通过重新启动定时 器继续进行周期性读取底层设备文件的过程。当然,这种方法适合于各对象对事件的处理时间较短,而底层设备发来消息的频率又相对较慢的情况。在这种情况下, 上述方法完全可以满足用户的需求,而又避免了处理一些与线程并发有关的复杂问题。

当然,利用定时器机制实现多线程编程在某些方面具有一定的局限性,有关到底如何实现多线程编程,如何编写出效率更高的代码,还有待于开 发者进一步研究和探讨。

参考资料

(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.

关于作者


续欣:联系方式 [email protected]

相关文章
  • Qt中的多线程编程 2010-08-18

    Qt中的多线程编程 级 别: 初级 续欣 ([email protected]), 2004 年 4 月 01 日 Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功能.为了满足用户构造复杂图形界面系统的需求,Qt 提供了丰富的多线程编程支持. Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功能.为了满足用户构造复杂图形界面系统的需求,Qt 提供了丰富的多线程编程支持.从 2.2 版本开始,Qt 主要从下面三个方

  • 详解Python中的多线程编程 2014-04-08

    这篇文章主要介绍了详解Python中的多线程编程,Python中的多线程一直是Python学习中的重点和难点,要反复巩固!需要的朋友可以参考下 一.简介 多线程编程技术可以实现代码并行性,优化处理能力,同时功能的更小划分可以使代码的可重用性更好.Python中threading和Queue模块可以用来实现多线程编程. 二.详解 1.线程和进程 进程(有时被称为重量级进程)是程序的一次执行.每个进程都有自己的地址空间.内存.数据栈以及其它记录其运行轨迹的辅助数据.操作系统管理在其上运行的所有进程,

  • Python中尝试多线程编程的一个简明例子 2014-12-13

    这篇文章主要介绍了Python中尝试多线程编程的一个简明例子,由于GIL的存在,Python中的多线程编程一个是热点和难点问题,需要的朋友可以参考下 综述 多线程是程序设计中的一个重要方面,尤其是在服务器Deamon程序方面.无论何种系统,线程调度的开销都比传统的进程要快得多. Python可以方便地支持多线程.可以快速创建线程.互斥锁.信号量等等元素,支持线程读写同步互斥.美中不足的是,Python的运行在Python 虚拟机上,创建的多线程可能是虚拟的线程,需要由Python虚拟机来轮询调度

  • nodejs中使用多线程编程的方法实例 2014-12-19

    这篇文章主要介绍了nodejs中使用多线程编程的方法实例,本文使用nodejs addon借助c/c++的能力扩展nodejs多线程编程,需要的朋友可以参考下 在以前的博文别说不可能,nodejs中实现sleep中,我向大家介绍了nodejs addon的用法.今天的主题还是addon,继续挖掘c/c++的能力,弥补nodejs的弱点. 我曾多次提到过nodejs的性能问题.其实就语言本身而言,nodejs的性能还是很高的,虽然不及大多部静态语言,但差距也并不大:相对其他动态语言而言,速度优势非

  • Qt 中的多线程(一) 2014-04-24

    QT通过三种形式提供了对线程的支持.它们分别是,一.平台无关的线程类,二.线程安全的事件投递,三.跨线程的信号-槽连接.这使得开发轻巧的多线程Qt程序更为容易,并能充分利用多处理器机器的优势.多线程编程也是一个有用的模式,它用于解决执行较长时间的操作而不至于用户界面失去响应.在Qt的早期版本中,在构建库时有不选择线程支持的选项,从4.0开始,线程总是有效的. 线程类 Qt 包含下面一些线程相关的类: QThread 提供了开始一个新线程的方法 QThreadStorage 提供逐线程数据存储 Q

  • Android 开发中的多线程编程技术 2013-02-16

    Java中的线程 Java的线程类是java.lang.Thread类.当生成一个Thread类的对象之后,一个新的线程就产生了.Java中每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run( )称为线程体. 下面是构建线程类几种常用的方法: public Thread() public Thread(Runnable target) public Thread(Runnable target, String name) public Thread(String

  • Qt 中的多线程(二) 2015-05-03

    可重入与线程安全 在Qt文档中,术语"可重入"与"线程安全"被用来说明一个函数如何用于多线程程序.假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是"可重入"的.假如不同的线程作用在同一个实例上仍可以正常工作,那么称之为"线程安全"的. 大多数c++类天生就是可重入的,因为它们典型地仅仅引用成员数据.任何线程可以在类的一个实例上调用这样的成员函数,只要没有别的线程在同一个实例上调用这个成员

  • Qt的多线程编程注意事项 2014-04-01

    QT总的来说是一个易学易用的库, 但是QT的多线程使用确实容易犯错,尤其是结合上异步的网络访问,谁用谁知道.我觉得核心是一句话,QThread要当作线程控制块用,不要以为派生一个QThread的子类,里面的东西都跑在新的线程里了,只有run()里的是这样. 下面这篇文章我觉得讲的很好,深入解析QT的线程用法,文章有点长,但是值得一读. 原文在 http://qt-project.org/wiki/ThreadsEventsQObjects 译文在 http://www.cppblog.com/b

  • C#多线程编程中的锁系统(二) 2014-02-24

    这篇文章主要介绍了C#多线程编程中的锁系统(二),本文讲解了volatile.Interlocked.ReaderWriterLockSlim等升级锁和原子操作的使用实例,需要的朋友可以参考下 上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了. 这一次我们说说升级锁和原子操作. 目录 1:volatile 2: Interlocked 3:ReaderWriterLockSlim 4:总结 一:volatile 简单来说: volatile关键字是告诉c#编译

  • C#多线程编程中的锁系统(四):自旋锁 2014-03-15

    这篇文章主要介绍了C#多线程编程中的锁系统(四):自旋锁,本文讲解了基础知识.自旋锁示例.SpinLock等内容,需要的朋友可以参考下 目录一:基础 二:自旋锁示例 三:SpinLock 四:继续SpinLock 五:总结 一:基础 内核锁:基于内核对象构造的锁机制,就是通常说的内核构造模式.用户模式构造和内核模式构造 优点:cpu利用最大化.它发现资源被锁住,请求就排队等候.线程切换到别处干活,直到接受到可用信号,线程再切回来继续处理请求. 缺点:托管代码->用户模式代码->内核代码损耗.线

  • python多线程编程中的join函数使用心得 2014-04-15

    这篇文章主要介绍了python多线程编程中的join函数使用心得,本文先是给出了join函数使用例子,并对join函数的使用作了总结,需要的朋友可以参考下 今天去辛集买箱包,下午挺晚才回来,又是恶心又是头痛.恶心是因为早上吃坏东西+晕车+回来时看到车祸现场,头痛大概是烈日和空调混合刺激而成.没有时间没有精神没有力气学习了,这篇博客就说说python中一个小小函数. 由于坑爹的学校坑爷的专业,多线程编程老师从来没教过,多线程的概念也是教的稀里糊涂,本人python也是菜鸟级别,所以遇到多线程的编程

  • C#多线程编程中的锁系统基本用法 2014-06-04

    这篇文章主要介绍了C#多线程编程中的锁系统基本用法,本文讲解了lock.Monitor.mutex.Semaphore等内容,需要的朋友可以参考下 平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述. 目录 一:lock.Monitor 1:基础. 2: 作用域. 3:字符串锁. 4:monitor使用 二:mutex 三:Semaphore 四:总结 一:lock.Monitor 1:基础 Lock是Monitor语法糖简化写法.Lock在IL会生成Monito

  • Java多线程编程中易混淆的3个关键字总结 2014-06-09

    这篇文章主要介绍了Java多线程编程中易混淆的3个关键字总结,本文总结了.volatile.ThreadLocal.synchronized等3个关键字,对这几个容易混淆概念的关键字分别做了讲解,需要的朋友可以参考下 概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在这里就简单的做个总结. 1.volatile volatile主要是用来在多线程中同步变量.

  • Python3中多线程编程的队列运作示例 2014-06-21

    这篇文章主要介绍了Python3中多线程编程的队列运作示例,文中用一个简单的例子展示了Python下尝试多线程时队列的进站出站是如何运作的,需要的朋友可以参考下 Python3,开一个线程,间隔1秒把一个递增的数字写入队列,再开一个线程,从队列中取出数字并打印到终端 #! /usr/bin/env python3 import time import threading import queue # 一个线程,间隔一定的时间,把一个递增的数字写入队列 # 生产者 class Producer(t

  • C#多线程编程中的锁系统(三) 2014-07-14

    这篇文章主要介绍了C#多线程编程中的锁系统(三),本本文主要说下基于内核模式构造的线程同步方式.事件.信号量以及WaitHandle.AutoResetEvent.ManualResetEvent等内容,需要的朋友可以参考下 本章主要说下基于内核模式构造的线程同步方式,事件,信号量. 目录 一:理论 二:WaitHandle 三:AutoResetEvent 四:ManualResetEvent 五:总结 一:理论 我们晓得线程同步可分为,用户模式构造和内核模式构造. 内核模式构造:是由wind

  • 初步讲解Ruby编程中的多线程 2014-07-15

    这篇文章主要介绍了初步讲解Ruby编程中的多线程,线程是各种编程语言学习当中的重点和难点,需要的朋友可以参考下 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程. 线程是程序中一个单一的顺序控制流程,在单个程序中同时运行多个线程完成不同的工作,称为多线程. Ruby 中我们可以通过 Thread 类来创建多线程,Ruby的线程是一个轻量级的,可以以高效的方式来实现并行的代码. 创建 Ruby 线程 要启动一个新的线程,只需要调用 Thread.new 即可: # 线程 #1 代码部

  • Python中的并发编程 2013-10-01

    简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执行,这个控制流被称为该进程的主线程.在任何给定的时刻,一个程序只做一件事情. 一个程序可以通过Python库函数中的os或subprocess模块创建新进程(例如os.fork()或是subprocess.Popen()).然而,这些被称为子进程的进程却是独立运行的,它们有各自独立的系统状态以及主线

  • linux多线程编程(五) 2013-10-22

    使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段.堆栈段和数据段,这是一种"昂贵"的多任务工作方式. 线程 线程是计算机中独立运行的最小单位,运行时占用很少的系统资源.可以把线程看成是操作系统分配CPU时间的基本单元.一个进程可以拥有一个至多个线程.它线程在进程内部共享地址空间.打开的文件描述符等资源.同时线程也有其私有的数据信息,包括:

  • linux c多线程编程实例代码 2014-01-10

    这篇文章主要介绍了linux系统中的c多线程编程实例,大家可以参考使用以下代码 直接看代码吧,代码里有注释 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <time.h> #define MAX 3 int number =0; pthread_t id[2]; pthread_mutex_t mut; //初始化静

  • 在Python下尝试多线程编程 2014-03-04

    这篇文章主要介绍了在Python下多线程编程的尝试,由于GIL的存在,多线程在Python开发领域一直是个热门问题,需要的朋友可以参考下 多任务可以由多进程完成,也可以由一个进程内的多线程完成. 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程. 由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程. Python的标准库提供了两个模块:thread和threa