在基于操作系统的程序设计中,在处理多任务时,可以有多种方法,但效率较高的当属线程方式,下面就来讨论一下在Qt中如何实现线程编程。

先来说一下什么是线程。线程(thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈,自己的寄存器环境,自己的线程本地存储。在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,从而提高了程序的执行效率。

下面就以一个时钟显示的程序来说明在Qt中如何使用线程。在时间显示中,由于显示需要重复不断地刷新,所以可把它放在一个线程中,以节约主线程的资源。本程序基于Qt4.8.7版本制作,程序运行后的界面如下所示。

制作的具体过程如下:

1、启动Qt Creator开发环境,然后点击“New Project”新建项目,会弹出如下所示的对话框。

2、按上图中的默认选项,点击“Choose...”按钮,会弹出下面的对话框。

3、在上图的对话框中,可设置项目的名称和存储的位置,可按自己要求更改或按默认值(注意路径中不要包含中文),点击“下一步”按钮,出现下面的界面。

4、上面的界面为已配置好的环境,可直接点击“下一步”按钮,出现下面的界面。

5、在以上对话框中,可更改项目的细节,可按自己要求更改或按默认值,点击“下一步”按钮,出现下面的界面。

6、在图中直接点击“完成”按钮,结束项目新建的过程,最后得到如下图所示的开发环境。

7、在上图中,单击“项目”中的最后一项“界面文件”左边的箭头,展开该项的内容,然后双击其下的名为“mainwindow.ui”的界面文件,会打开界面设计窗口,如下图所示。

8、在设计界面中,默认有一个空的窗体,现在在左边的控件框中往下找到“Display Widgets”类,然后在其下找到“LCD Number”控件,并把它拖曳至窗体的中央,把长度设置为200像素,高度为60像素,如下图所示。

9、上述设计完成后,点击最左边工具条上的“编辑”切换到程序设计界面,展开“源文件”项,双击主窗体文件“mainwindow.cpp”把它打开,如下图所示。

10、在主程序中,加入对显示控件“LCD Number”的配置,修改后的主函数如下所示:

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lcdNumber->setDigitCount(8); //设置lcd里面的个数,格式是hh:mm:ss,总的是八个。所以设置为8
ui->lcdNumber->setFixedSize(220, 60); //设置大小
ui->lcdNumber->setPalette(Qt::cyan); //设置颜色
ui->lcdNumber->setSegmentStyle(QLCDNumber::Flat); //设置LCD字符为平面显示,无立体感
ui->lcdNumber->setStyleSheet("border: 1px solid black; color: black; background: silver;"); //设置LCD样式
TimeshowThread = new timeshowThread; //新建时间处理线程
connect(TimeshowThread, SIGNAL(sendTime(QString)), this, SLOT(displayTime(QString))); //连接时间显示槽函数
TimeshowThread->start();  //启动线程
}

11、然后加入一个新的函数,内容如下:

void MainWindow::displayTime(QString time) //时间显示槽函数
{
ui->lcdNumber->display(time);
}

12、改好的主程序界面如下所示。

13、 接下来点击“项目”中的“头文件”左边箭头展开该项,双击头文件“mainwindow.h”把它打开,如下图所示。

14、在上述头文件中,加入时间显示槽函数的申明,修改后的头文件如下所示:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "timeshowthread.h"  //新加入线程程序的头文件
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
timeshowThread *TimeshowThread;  //新建时间显示线程
private slots:
void displayTime(QString);  //新加入的槽函数申明
};
#endif // MAINWINDOW_H

15、接下来要新建线程文件,在左边的“源文件”上点击右键,选择“添加新文件...”一项,会弹出如下图的对话框。

16、在上图中选择“C++Source File”一项,然后点击“Choose...”按钮,会弹出如下对话框。

17、在上图中填入新建的线程程序文件名称,这里填入“timeshowthread”,然后点击“下一步”按钮,会弹出如下对话框。

18、在上图中点击“完成”按钮,得到的界面如下所示。

19、在线程文件timeshowthread.cpp中加入以下内容:

#include "timeshowthread.h"
#include <QTime>
timeshowThread::timeshowThread()
{

}
void timeshowThread::run() //线程程序
{
while(1)
{
QDateTime current_date_time = QDateTime::currentDateTime(); //获取当前时间
QString current_time = current_date_time.toString("hh:mm:ss"); //格式化
emit sendTime(current_time); //发射信号
msleep(20); //延时
}
}

20、接下来新建线程头文件,在左边的“头文件”上点击右键,选择“添加新文件...”一项,会弹出如下图的对话框。

21、在上图中选择“C++Header File”一项,然后点击“Choose...”按钮,会弹出如下对话框。

22、在上图中填入新建的线程程序头文件名称,这里填入“timeshowthread”(要与线程程序名称一致),然后点击“下一步”按钮,会弹出如下对话框。

23、在上图中点击“完成”按钮,得到的界面如下所示。

24、在线程头文件timeshowthread.h中加入以下内容:

#ifndef TIMESHOWTHREAD_H
#define TIMESHOWTHREAD_H
#include <QThread>
#include <QString>
class timeshowThread : public QThread
{
Q_OBJECT
public:
timeshowThread();  //申明线程函数
signals:
void sendTime(QString);  //申明信号发送函数
protected:
void run();  //申明线程执行函数
public slots:
};
#endif // TIMESHOWTHREAD_H

25、保存所有文件,点击运行按钮运行程序,将得到如前面所示的时钟界面。(注:程序能否正常运行还取决于开发环境的配置)

接下来简要说明一下线程的创建原理。

在Qt中提供有QThread类,要创建一个新的线程,只需定义一个类(如上述头文件timeshowthread.h中定义的timeshowThread),让其继承QThread类即可。然后使用自己创建的类(如timeshowThread)实例化一个对象,也就是用该类创建一个变量(如上述头文件mainwindow.h中的timeshowThread *TimeshowThread;一句及主程序文件mainwindow.cpp中的TimeshowThread = new timeshowThread;一句)。在QThread类中有一个虚函数QThread::run(),要实现自己的线程代码只需要重写该函数即可。最后,通过QThread类中的start()函数可启动这个线程(如TimeshowThread->start();)。另外,由于本例中只进行时间的显示一个任务,所示没有线程结束的操作,时间显示线程将同主线程(UI线程)一起结束。若要进行线程结束的操作,可执行“TimeshowThread->terminate();”即可。

在上面的例子中,还定义了一个信号sendTime(QString)和一个槽函数displayTime(QString time),并把两个量联系在一起(执行connect(TimeshowThread, SIGNAL(sendTime(QString)), this, SLOT(displayTime(QString)));一句)。这样一来, 当在线程中获取了当前时间量后,通过执行“emit sendTime(current_time);”一句,把当前时间量通过信号发送给槽函数displayTime,然后在槽函数中进行显示(执行ui->lcdNumber->display(time);一句)。在线程程序中,通过死循环来不断更新获取当前的时间量,并通过信号发送给槽函数进行显示,这样一来,由于死循环放在了线程中,并不会拖累主线程。但是,虽然死循环放在线程中,但也不能执行的太“满”,否则还是会占用过多的CPU资源,所以在上面的实例中,还是进行了一定时间的休眠(如执行msleep(20);一句)。这里要强调一点,具体休眠多长时间应根据实际的运行情况而定,因为线程通过操作系统来调度,所以休眠时间并不精确,并且休眠时间应以不过度消耗CPU资源为佳(当然,在此例中,由于时间以秒来更新,所以休眠最长可取1秒)。

Qt中的线程编程的更多相关文章

  1. QT中的SOCKET编程(QT-2.3.2)

    转自:http://mylovejsj.blog.163.com/blog/static/38673975200892010842865/ QT中的SOCKET编程 2008-10-07 23:13 ...

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

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

  3. QT中的SOCKET编程

    转自:http://mylovejsj.blog.163.com/blog/static/38673975200892010842865/ QT中的SOCKET编程 2008-10-07 23:13 ...

  4. Qt中的多线程编程

    http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...

  5. Qt入门(9)——Qt中的线程支持

    Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法.警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心 ...

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

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

  7. QT中的线程与事件循环理解(1)

    1.需要使用多线程管理的例子 一个进程可以有一个或更多线程同时运行.线程可以看做是“轻量级进程”,进程完全由操作系统管理,线程即可以由操作系统管理,也可以由应用程序管理.Qt 使用QThread 来管 ...

  8. Qt中的串口编程之三

    QtSerialPort 今天我们来介绍一下QtSerialPort模块的源代码,学习一下该可移植的串口编程库是怎么实现的. 首先,我们下载好了源代码之后,使用QtCreator打开整个工程,可以看到 ...

  9. Qt中暂停线程的执行

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

  10. Qt中暂停线程的执行(利用QMutex,超级简单明了)

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

随机推荐

  1. 面向对象基础之基础—控制台C#模拟银行ATM存取操作实例

    c#控制台应用程序ATM银行操作实例.主要介绍了设计的方法:使用的类介绍:具体的运行界面:程序代码.代码直接写在一起放在Programm.cs中,拷贝可直接运行. 一.设计 1.原则上采用三层:(1) ...

  2. fastlane iOS打包 安装与使用

    1.安装 fastlane sudo gem install fastlane 2.进入工程目录下 终端输入 fastlane init 接着输入3 然后control + c退出 接着编写 Fast ...

  3. 第1关—print()函数与转换

    Print()函数的用法有以下几种:单枪匹马--不带引号.搭配单引号.搭配双引号.搭配三引号,我们逐个见识下吧!  1.无引号       注意,这里的括号一定要用[英文括号],不然会报错 impor ...

  4. TypeScript的super

    (function(){ class Animal{ name:string; constructor(name:string){ this.name=name; } sayHello(){ cons ...

  5. CTreeCtrl的用法汇总(转)

    一 基础操作  1 插入节点 1)插入根节点 //插入根节点 HTREEITEM hRoot; CString str=L"ROOT" hRoot=nTreeCtrl.Insert ...

  6. appcrawler(2.1.3)采坑

    转发:https://www.jianshu.com/p/d97290136bad 遇到的坑: 问题1: Exception in thread "main" org.openqa ...

  7. Stream流常用API

    文档 https://www.runoob.com/java/java8-streams.html JDK8 Stream API: https://docs.oracle.com/javase/8/ ...

  8. Jmeter面试

    jmeter面试题及答案 1.解释什么是jmeter? jmeter是一款java开源工具,用于性能负载测试.它旨在分析和衡量web应用程序和各种服务的性能和负载功能行为. 2.说明jmeter的工作 ...

  9. 手机、ESP32单片机、MQTT服务器连接实验

    实验目的:通过自己制作的手机APP,连接MQTT服务器,获取ESP32单片机的温湿度信息,控制ESP32单片机上的LED开关. 实验准备: 1.硬件:ESP32开发板(型号不限,esp01s也可以). ...

  10. ES6-Promise下

    六.实例的finally方法: promise状态发生变化指的是由初始态变成成功或者失败态 处理错误指的是没有调用catch(失败态实例调用可以执行其回调的then) finally里面的回调函数只要 ...