Qt对线程提供了支持,基本形式有独立于平台的线程类、线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法。

警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心类(比如,QProcess)和网络类都不是线程安全的。

QRegExp使用一个静态缓存并且也不是线程安全的,即使通过使用QMutex来保护的QRegExp对象。

启用线程支持
在Windows上安装Qt时,在一些编译器上线程支持是一个选项。
在Mac OS X和Unix上,线程支持可以当你在运行configure脚本时添加-thread选项就可以生效了。在Unix平台上,多线程程序必须用特殊的方式连接,比如使用特殊的libc,安装程序将会创建另外一个库libqt-mt并且因此线程程序必须和这个库进行连接(使用-lqt-mt)而不是标准的Qt库。
在两个平台上,你都应该定义宏QT_THREAD_SUPPORT来编译(比如,编译时使用-DQT_THREAD_SUPPORT)。在Windows上,这个通常可以在qconfig.h写一个条目来解决。

线程类
最重要的类是QThread,也就是说要开始一个新的线程,就是开始执行你重新实现的QThread::run()。这和Java的线程类很相似。
为了写线程程序,在两个线程同时希望访问同一个数据时,对数据进行保护是很必要的。因此这里也有一个QMutex类,一个线程可以锁定互斥量,并且在它锁定之后,其它线程就不能再锁定这个互斥量了,试图这样做的线程都会被阻塞直到互斥量被释放。例如:

  1. class MyClass
  2. {
  3. public:
  4. void doStuff( int );
  5.  
  6. private:
  7. QMutex mutex;
  8. int a;
  9. int b;
  10. };
  11.  
  12. // 这里设置a为c,b为c*2。
  13.  
  14. void MyClass::doStuff( int c )
  15. {
  16. mutex.lock();
  17. a = c;
  18. b = c * 2;
  19. mutex.unlock();
  20. }

这保证了同一时间只有一个线程可以进入MyClass::doStuff(),所以b将永远等于c * 2。

另外一个线程也需要在一个给定的条件下等待其它线程的唤醒,QWaitCondition类就被提供了。线程等待的条件QWaitCondition指出发生了什么事情,阻塞将一直持续到这种事情发生。当某种事情发生了,QWaitCondition可以唤醒等待这一事件的线程之一或全部。(这和POSIX线程条件变量是具有相同功能的并且它也是Unix上的一种实现。)例如:

  1. #include <qapplication.h>
  2. #include <qpushbutton.h>
  3.  
  4. // 全局条件变量
  5. QWaitCondition mycond;
  6.  
  7. // Worker类实现
  8. class Worker : public QPushButton, public QThread
  9. {
  10. Q_OBJECT
  11.  
  12. public:
  13. Worker(QWidget *parent = 0, const char *name = 0)
  14. : QPushButton(parent, name)
  15. {
  16. setText("Start Working");
  17.  
  18. // 连接从QPushButton继承来的信号和我们的slotClicked()方法
  19. connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
  20.  
  21. // 调用从QThread继承来的start()方法……这将立即开始线程的执行
  22. QThread::start();
  23. }
  24.  
  25. public slots:
  26. void slotClicked()
  27. {
  28. // 唤醒等待这个条件变量的一个线程
  29. mycond.wakeOne();
  30. }
  31.  
  32. protected:
  33. void run()
  34. {
  35. // 这个方法将被新创建的线程调用……
  36.  
  37. while ( TRUE ) {
  38. // 锁定应用程序互斥锁,并且设置窗口标题来表明我们正在等待开始工作
  39. qApp->lock();
  40. setCaption( "Waiting" );
  41. qApp->unlock();
  42.  
  43. // 等待直到我们被告知可以继续
  44. mycond.wait();
  45.  
  46. // 如果我们到了这里,我们已经被另一个线程唤醒……让我们来设置标题来表明我们正在工作
  47. qApp->lock();
  48. setCaption( "Working!" );
  49. qApp->unlock();
  50.  
  51. // 这可能会占用一些时间,几秒、几分钟或者几小时等等,因为这个一个和GUI线程分开的线程,在处理事件时,GUI线程不会停下来……
  52. do_complicated_thing();
  53. }
  54. }
  55. };
  56.  
  57. // 主线程——所有的GUI事件都由这个线程处理。
  58. int main( int argc, char **argv )
  59. {
  60. QApplication app( argc, argv );
  61.  
  62. // 创建一个worker……当我们这样做的时候,这个worker将在一个线程中运行
  63. Worker firstworker( 0, "worker" );
  64.  
  65. app.setMainWidget( &worker );
  66. worker.show();
  67.  
  68. return app.exec();
  69. }

只要你按下按钮,这个程序就会唤醒worker线程,这个线程将会进行并且做一些工作并且然后会回来继续等待被告知做更多的工作。如果当按钮被按下时,worker线程正在工作,那么就什么也不会发生。当线程完成了工作并且再次调用QWaitCondition::wait(),然后它就会被开始。

线程安全的事件传递
在Qt中,一个线程总是一个事件线程——确实是这样的,线程从窗口系统中拉出事件并且把它们分发给窗口部件。静态方法QThread::postEvent从线程中传递事件,而不同于事件线程。事件线程被唤醒并且事件就像一个普通窗口系统事件那样在事件线程中被分发。例如,你可以强制一个窗口部件通过如下这样做的一个不同的线程来进行重绘:

  1. QWidget *mywidget;
  2. QThread::postEvent( mywidget, new QPaintEvent( QRect(0, 0, 100, 100) ) );

这(异步地)将使mywidget重绘一块100*100的正方形区域。

Qt库互斥量
Qt库互斥量提供了从线程而不是事件线程中调用Qt方法的一种方法。例如:

  1. QApplication *qApp;
  2. QWidget *mywidget;
  3.  
  4. qApp->lock();
  5.  
  6. mywidget->setGeometry(0,0,100,100);
  7.  
  8. QPainter p;
  9. p.begin(mywidget);
  10. p.drawLine(0,0,100,100);
  11. p.end();
  12.  
  13. qApp->unlock();

在Qt中没有使用互斥量而调用一个函数通常情况下结果将是不可预知的。从另外一个线程中调用Qt的一个GUI相关函数需要使用Qt库互斥量。在这种情况下,所有可能最终访问任何图形或者窗口系统资源的都是GUI相关的。使用容器类,字符串或者输入/输出类,如果对象只被一个线程使用就不需要任何互斥量了。

当进行线程编程时,需要注意的一些事情:

当使用Qt库互斥量的时候不要做任何阻塞操作。这将会冻结事件循环。
确认你锁定一个递归QMutex的次数和解锁的次数一样,不能多也不能少。
在调用除了Qt容器和工具类的任何东西之前锁定Qt应用程序互斥量。
谨防隐含地共享类,你应该避免在线程之间使用操作符=()来复制它们。这将会在Qt的未来主要的或次要的发行版本中进行改进。
谨防那些没有被设计为线程安全的Qt类,例如,QPtrList的应用程序接口就不是线程安全的并且如果不同的线程需要遍历一个QPtrList,它们应该在调用QPtrList::first()之前锁定并且在到达终点之后解锁,而不是在QPtrList::next()的前后进行锁定和解锁。
确认只在GUI线程中创建的继承和使用了QWidget、QTimer和QSocketNotifier的对象。在一些平台上,在某个不是GUI线程的线程中创建这样的对象将永远不会接受到底层窗口系统的事件。
和上面很相似,只在GUI线程中使用QNetwork类。一个经常被问到的问题是一个QSocket是否可以在多线程中使用。这不是必须得,因为所有的QNetwork类都是异步的。
不要在不是GUI线程的线程中试图调用processEvents()函数。这也包括QDialog::exec()、QPopupMenu::exec()、QApplication::processEvents()和其它一些。
在你的应用程序中,不要把普通的Qt库和支持线程的Qt库混合使用。这也就是说如果你的程序使用了支持线程的Qt库,你就不应该连接普通的Qt库、动态的载入普通Qt库或者动态地连接其它依赖普通Qt库的库或者插件。在一些系统上,这样做会导致Qt库中使用的静态数据变得不可靠了。

Qt入门(9)——Qt中的线程支持的更多相关文章

  1. Qt入门学习——Qt 5 帮助文档的使用

    Qt入门学习——Qt 5 帮助文档的使用 学习图形界面开发,肯定离不开帮助文档的使用,因为它不像 C 语言那样就那么几个函数接口,图形接口的接口可以用海量来形容,常用的我们可能能记住,其它的真的没有必 ...

  2. Qt入门——使用QT+VS2008开发windows应用程序

    1.文件->新建->项目 Qt4 Projects 右边已安装模板当中选择At Application. 确定 2.选择需要使用的QT库 下一步 3. “class name”:指定类的名 ...

  3. 【Qt开发】事件循环与线程 二

    事件循环与线程 二 Qt 线程类 Qt对线程的支持已经有很多年了(发布于2000年九月22日的Qt2.2引入了QThread类),Qt 4.0版本的release则对其所有所支持平台默认地是对多线程支 ...

  4. QT中的线程与事件循环理解(2)

    1. Qt多线程与Qobject的关系 每一个 Qt 应用程序至少有一个事件循环,就是调用了QCoreApplication::exec()的那个事件循环.不过,QThread也可以开启事件循环.只不 ...

  5. Qt中暂停线程的执行(主线程和工作线程共用一把锁,一旦主线程将它锁上,工作线程就无法运行了,这也是一个办法)

    在线程中定义一个信号量: QMutex pause; 把run()函数中循环执行的部分用信号量pause锁住:   void run()   {   while(1)   {   pause.lock ...

  6. qt 如何使用 lamda 表达式接收线程中发射的数据,并在里面更新 UI ?

    Qt 信号和槽连接方式 常量 描述 Qt::AutoConnection (默认)如果接收方位于发出信号的线程中,则使用Qt::DirectConnection.否则,使用Qt::QueuedConn ...

  7. 第15.28节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QTableWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 表格部件为应用程序提供标准的表格显示工具,在表格内可以管理基于行和列的数据项,表格中的最大 ...

  8. 第15.27节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QTreeWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 树部件(Tree Widget)是Qt Designer中 Item Widgets(It ...

  9. 第15.26节 PyQt(Python+Qt)入门学习:Model/View架构中的便利类QListWidget详解

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 列表部件(List Widget)对应类QListWidget,是从QListView派生 ...

随机推荐

  1. Android 使用Android Studio + Gradle 或 命令行 进行apk签名打包

    官方文档:https://developer.Android.com/tools/publishing/app-signing.html 1. 默认为debug mode,使用的签名文件在: $HOM ...

  2. 响应式WEB设计

    近期在学习有关响应式设计的内容,对此做了些整理,图片来源于网络,附上自己做的简单demo,没有js,只用CSS做了简单的搭建http://y.zhso.net/. 1.为什么需要响应式web设计 出于 ...

  3. JavaScript正则验证数字、英文、电话号、身份证号、邮箱地址、链接地址等

    验证是否为数字:/^[0-9]*$/验证是否为汉字:/^[\u4e00-\u9fa5],{0,}$/验证x-y位的数字:/^\d{x,y}$/验证由26个英文字母组成的字符串:/^[A-Za-z]+$ ...

  4. Android开发手记(26) Java多线程的实现

    随着多核CPU的发展,多线程编程显得越来越重要,本文将对Java中的多线程编程进行一些简单的探讨. 1.继承Thread类 Java中,线程运行的基本单位是Thread,所以,我们可以通过继承Thre ...

  5. C#中的Dictionary字典类介绍

      Dictionary字典类介绍 必须包含名空间System.Collection.Generic    Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值)    键必须是 ...

  6. sql查询过程中 update,insert,delete可视化收影响行数

    insert into test_tb output inserted.id,inserted.data values('c'),('d') delete from test_tb output de ...

  7. 项目中常用SQL语句总结

    1.项目中常常需要修改字段长度,但需要保留数据--增加业务受理 项目名称 字段长度alter table t_ywsl add aa varchar2(200);update t_ywsl set a ...

  8. 自己制作精美的App Store 软件截屏

    当用户搜索到App的时候,一般都会先看截图,如果截图效果不好,可能用户就不会下载. 不想自己辛苦写的认为还不错的软件,因为截图的原因,而降低了很多下载量吧. 轻轻松松做出这样高大上的截屏效果来. Sc ...

  9. JQUERY1.9学习笔记 之可见性过滤器(二) 可见选择器

    描述:选择所有可见的元素. 例:点击时让所有的可见的div元素变黄. <!doctype html><html lang="en"> <head> ...

  10. 定制linux中的Gtk theme<一>如何设置窗口按钮的多态效果

    GTK主题之个人理解: GTK 主题引擎(包含代码所需的图形元素) +  主题配置文件(gtkrc文件)+ 数据资源文件(如图片等)    三者所呈现给用户的视觉风格效果 GTK拥有一套大量的widg ...