在基于操作系统的程序设计中,在处理多任务时,可以有多种方法,但效率较高的当属线程方式,下面就来讨论一下在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. VS2022 17.1.6在windows10下打开winform设计器报timed out while connecting to named pipe错误

    .net 6.0的项目,vs2022 17.1.6在windows10下打开winform设计器报timed out while connecting to named pipe错误,同样的项目在wi ...

  2. LinuxK8S集群搭建三(部署dashboard)

    系统环境: CentOS 7 64位 准备工作: 通过虚拟机创建三台CentOS服务器,可参照之前的文章192.168.28.128 --master192.168.28.130 --node0119 ...

  3. js判断任意数值接近数组中的某个值

    可以是数组,也可以是数组对象,看需求定义 let val = '' for (let i = 0; i < this.allData.length; i++) { if (this.days & ...

  4. AgilePoin规则执行

    我在写窗体规则时,明明默认值已经绑定,但是在页面加载时规则并没有起作用,导致改隐藏的没隐藏,该显示的不显示.找了半天,发现规则设置时可选择执行事件. 设置在页面加载时执行规则后,发现还是不能正确显隐组 ...

  5. Kongmaster

    圣人处无为之事 大智若愚 穷则独善其身,达则兼济天下

  6. Java基础学习:6、方法的重载和可变参数

    一.方法重载: 基本介绍: Java中允许在一个类中,多个同名方法的存在,到要求形参列表不一致. 注意事项: 1.方法名:必须一致. 2.形参列表:必须不同(形参类型或个数或顺序至少有一样不同,参数名 ...

  7. OnMicro BLE应用方案|蓝牙语音遥控器-OM6621E

    随着物联网技术不断发展,家用电器往智能化方向持续迭代,使用红外遥控器这种传统的互动方式已经满足不了实际的使用需求,蓝牙语音遥控器作为人机交互新载体,逐渐取代传统红外遥控器成为家居设备的标配. 相比于传 ...

  8. Nginx的重写功能——Rewrite

    Nginx的重写功能--Rewrite https://huaweicloud.csdn.net/63566cced3efff3090b5f470.html?spm=1001.2101.3001.66 ...

  9. go 的 wire 依赖注入

    首先安装 wire 工具 go install github.com/google/wire/cmd/wire@latest go 文件 package main import ( "fmt ...

  10. 【java数据结构与算法】插入排序

    [插入排序解析]起始:假设第一个元素为已经排好序那么我们就要从数组的第二个元素开始每一轮确定1一个元素的正确位置所以外层循环的控制变量为 [1,arr.length)的左闭右开区间外层循环控制比较轮次 ...