Qt多线程-转载

来源:互联网 发布:网络推广的各种渠道 编辑:程序博客网 时间:2024/05/25 19:56

 

http://www.cuteqt.com/blog/?p=547

原文链接:Qt的多线程之一 — 后台创建缩略图的例子(附例子)

 

关于多线程可讲的东西实在太多了,从多线程本身的概念,到Qt的多线程相关的类, 再到多线程编程相关的内容,方方面面, 不是一两篇贴子能讲明白的。 笔者准备尽我所能写一些通俗易懂的文章, 介绍一下和Qt多线程相关的内容, 争取写上几篇凑成一个系列(但不保证会写几篇,按照笔者的懒惰程度,也许第二篇永远不会面世也不一定…)。 本来开篇的帖子应该八一八多线程概念性的东西,不过为了应景,先来一个使用Qt多线程创建缩略图的小例子。

起因是在qtcn上看到有人说创建缩略图的API执行很慢,并且阻塞了gui的正常运转,需要改成多线程来实现, 但他不清楚如何实现,希望能有个例子。 这位同学的问题揭示了Qt里最需要用到多线程的一个用例,那就是防止一切阻塞GUI线程的操作。 Qt GUI编程有个最基本但很多人不熟悉的原则是所有的操作都不能是阻塞的, 同时所有的操作都不能占用很多CPU, 这个原则是针对GUI线程来说的, 但后者对子线程也同样适用(这一点我们后边继续八)。

 

熟悉Qt的人都应该有所了解, Qt的图形处理是基于事件循环的, 这一点和大多数的GUI工具库一致。 事件循环简单来说就是个死循环, 里面读取系统外设的事件, 然后见招拆招。 Qt里的Events, Signals, Timer都在事件循环里分发, 所以,如果阻塞了事件循环用脚趾头想想也能知道会有多么严重的后果, 最明显的就是界面不刷新,也不能和用户交互, 所以这就要求我们在写程序的时候不管是事件处理函数还是槽函数里都不能进行占用CPU时间的操作, 特别要杜绝阻塞性的操作如串口数据读取、杜绝长时间的高CPU占用率的操作如死循环和大数据量的计算等。 However, “人在河边走, 哪能不湿鞋”呢, 总有需要用到这些操作的时候, Qt也提供了解决方案, 那就是多线程。

Qt的多线程类有很多, 改天开帖详细介绍, 这里只八最核心的QThread类。 该类的用法是Posix多线程的用法的简化版, 只需要派生一个QThread的子类,实现其中的run虚函数就大功告成, 用的时候创建该类的实例,调用它的start方法, 理论上非常简单。 笔者个人觉得写多线程的程序首先要去恶补一下多线程的知识, Qt说到底只是一个工具,它的API包装得再好也得用的人懂得如何去使用才能发挥最好的作用。 Qt的文档中关于多线程的编程文章挺多, 另外还列出了一系列的推荐读物, 想把多线程程序写好的人一定要看。

前面啰嗦了这么多无非是想告诉大家,写多线程的程序单靠Qt的知识是不够的(更别说有些人还没用过Qt呢), 多线程的知识更加重要。 而且单靠一两篇简单的文章是学不明白的,你看人家多线程编程能写一大章书呢! 好了, 强调一下背景知识的重要性,下面要提供一个最简单的Qt多线程的例子程序, 它的功能是打开多个图片文件之后创建线程执行图片缩放的操作, 创建图片的缩略图,完成后子线程会把缩略图传给GUI线程,由GUI线程创建Label控件来显示缩略图。 各位看官注意了,这个例子很简单, 和多线程相关的部分其实很少, 没有用到Mutex也没涉及并发等等概念, 单一的演示如何防止大数据量的计算阻塞GUI线程。

例子的源码见附件,这里先列出一些关键的流程和代码, 方便大家理解:

第一步:打开图片文件
用QFileDialog::getOpenFileNames获得图片文件名的StringList

第二步:遍历List创建线程

void MainWin::createThumbnail(const QString& filename){QThread* thread = new ThumbnailThread(filename, 10 - waitseconds);connect(thread, SIGNAL(thumbnailFinished(QImage)), this, SLOT(addThumbnail(QImage)));connect(thread, SIGNAL(thumbnailFailed(const QString)), this, SLOT(showError(const QString)));connect(thread, SIGNAL(finished()), this, SLOT(deleteThread()));thread->start();}

finished是QThread类自带的信号,在线程结束执行时发出; 其他两个信号是自定义信号, 用于在GUI线程和子线程之间传递数据。

第三步:子线程内创建缩略图
使用了QImage的scaled方法做图片缩放。

void ThumbnailThread::run(){if( bigpm.isNull()){emit thumbnailFailed(pmfilename);}else{smallpm = bigpm.scaled(TN_WIDTH, TN_HEIGHT, Qt::KeepAspectRatio);emit thumbnailFinished(smallpm);}}

第四步:GUI线程处理thumbnailFinished信号

为每个图片创建一个Label, 将之加到预先定义好的GridLayout中

void MainWin::addThumbnail(QImage smallpm){static int i = 0;static int j = 0;QLabel* label = new QLabel;label->setPixmap(QPixmap::fromImage(smallpm));QGridLayout* gl = qobject_cast(previewwidget->layout());gl->addWidget(label, j, i);label->show();qWarning() << "Label:" <

关键点提示:
- Qt的signal可跨线程传递, 但要注意slot执行的线程。 connect时可以在第五个参数的位置指定连接的属性(这里用了默认值), 如DirectConnection表示在connect函数执行的线程里执行slot, QueuedConnection表示slot执行在接受者所在的线程, (还有其他选项参看文档)本例中的slot都执行在GUI线程中。

- 在收到finished信号后要清除执行完毕的线程, 为了防止删除后对线程实例的访问这里用了个deleteLater方法 — 在大家都不再访问thread实例时再删除。

- Qt中所有和GUI相关的操作都要放在GUI线程里执行,包括所有Widget的创建和访问,QPixmap的类也算GUI的类, 所以本例中只能用QImage来处理图片。

- 这个例子其实用一个子线程就够了,给每张图片创建一个线程有点浪费。 例子里有些为了测试需要加的代码可能会影响阅读(比如那个waitseconds), 请大家自动忽略。 比如为了更好的看到效果,给线程里加了个sleep延缓处理图片的速度…

threadthumbnail.tar

有问题blog或bbs留言再讨论。

Tags: example, qimage, Qt, thread, thumbnail
This entry was posted on Tuesday, July 7th, 2009 at 2:26 PM and is filed under Qt技术. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

7 Responses to “Qt的多线程之一 — 后台创建缩略图的例子(附例子)”

  1. huang_yanjun says:

    谢谢,受益匪浅。

    Reply
  2. nacllv says:

    如果我的工程文件里是加载ui界面的那种,线程要怎么设。我之前遇到过界面冻结的情况,应该就是多线程的原因,那时候找了好久,看到有人说是去掉了多线程,重新编译库,这样确实没有界面冻结了,但是运行速度很慢,我想还是要用多线程,不知道有没有界面是ui的那种多线程的例子呢。我需要怎么把gui弄到单个线程里

    Reply
  3. shiroki says:

    兄弟大概还是没有好好看我的文章吧? 我在后面写了,
    - Qt中所有和GUI相关的操作都要放在GUI线程里执行,包括所有Widget的创建和访问,QPixmap的类也算GUI的类, 所以本例中只能用QImage来处理图片。

    所以你的要求是实现不了的。 想别的办法吧~

    Reply
  4. Ice says:

    我遇到一个关于缩略图的问题, 我交叉编译的qtopia拿到板子上跑,只能显示jpeg格式的缩略图,其他格式的图片都不能显示阿! 请高手指点一下,谢谢了!

    Reply
  5. shiroki says:

    png和bmp是内建的格式, 不会不能显示吧。 其他的格式是插件形式提供的, 要看看相应的插件有没有。

    Reply
  6. yinthena says:

    你好,有qq号么?
    我的主界面在做实时图形显示 再打开一个子窗口时,由于子窗口创建大量控件,所以影响主界面的实时显示,有什么好办法呢?

    Reply
  7. shiroki says:

    这种情况的话还是把子窗口提前创建好吧。 有问题去bbs上问

    Reply
原创粉丝点击