版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Qt6.2/C++程序设计与桌面应用开发21世纪高等学校计算机类课程创新规划教材–微课视频版教材目录第1章初识Qt框架第2章Qt开发基础第3章界面设计组件第4章主框架窗口第5章对话框设计第6章事件系统第7章文件与数据库第8章模型/视图结构第9章图形绘制第10章多媒体编程第11章网络编程第12章进程与线程教材目录第12章进程与线程12.1相关Qt类12.2进程12.3线程12.4线程控制12.1相关Qt类Qt对进程和线程的支持是通过一系列的类协同实现的,其中主要的有QProcess类和QThread类。QProcess类用来启动一个进程并与其进行通信;QThread类提供不依赖于平台的管理线程常用方法。12.1.1QProcess类QProcess类属于Qt的core模块中,它是QIODevice类的直接子类,属于Qt的顺序访问I/O设备;同时,QProcess类也是QObject的子类,因而具有Qt的信号/槽功能。其继承关系如图12.1所示。QProcess类提供了大量的函数实现进程的启动、控制、查询、设置及通信等功能。其部分成员函数及功能如表12.1所示。下面是一段使用QProcess启动cmd.exe控制台程序并执行dir命令的示例代码。12.1.2QThread类QThread类直接继承自QObject类,每个QThread对象代表了一个在应用程序中可以独立控制的线程,这个线程与进程中的其他线程分享数据。表12.2给出了QThread类的部分非继承成员函数及功能。下面是一段使用QThread类实现多线程的示例代码。12.2进程进程(Process)是计算机中的程序关于数据集合上的一次运行活动,是正在运行的程序的实例。从理论角度来看,进程是对正在运行的程序过程的抽象;从实现角度来看,进程就是一种数据结构。进程清晰地刻画了动态系统的内在规律,并有效地管理和调度进入计算机系统主存储器运行的程序。12.2.1进程的启动进程是一个“执行中的程序”,所以,启动进程就是开始运行一个程序。可以使用QProcess类的start()、startDetached()和execute()函数来启动一个进程。下面是一个在Qt应用程序中打开/关闭Windows系统计算器的简单实例。【例12.1】编写一个Qt应用程序,在程序中运行Windows计算器。(1)打开QtCreator集成开发环境,创建一个基于QWidget类的Qt应用程序。项目名称为examp12_1。(2)双击项目视图中的widget.ui界面文件,打开QtDesigner设计工具,对程序主窗口界面进行设计。在主窗口中添加1个QPlainTextEdit类型的多文本编辑器,和2个QPushButton类型的按钮。3个对象的名称分别为plainTextEdit、startButton和closeButton。(3)为2个QPushButton按钮添加clicked()信号的槽函数,函数名称分别为on_startButton_clicked()和on_closeButton_clicked()。(4)打开widget.h头文件,为Widget类添加一个名为isActive()的私有成员函数,用于判断计算器是否已经启动;添加一个名为showError()的槽函数,用于显示启动进程时可能会出现的错误信息;添加3个名称为myProcess、program和arguments的私有成员变量,分别表示进程、外部程序以及命令行参数。代码如下所示。(5)打开widget.cpp文件,编写构造函数、自定义函数,以及槽函数代码,以实现程序功能。如下所示。(6)构建并运行程序。12.2.2进程间通信Qt提供了多种方法在Qt应用程序中实现进程间通信(IPC,Inter-ProcessCommunication)。主要有:1、TCP/IP方法2、LocalServer/Socket方法3、SharedMemory方法4、D-Bus协议方法5、QProcess方法6、SessionManagement方法1、TCP/IP方法跨平台的QtNetwork模块提供了众多的类来实现网络编程。它不仅提供了使用特定应用程序级协议进行通信的高级类(如QNetworkAccessManager),也提供了用于实现相关协议的低级类(如QTcpSocket、QTcpServer、QSslSocket)。本教材的第11章网络编程中详细介绍了此种方法。2、LocalServer/Socket方法跨平台的QtNetwork模块提供了使本地网络编程可移植且容易的类。它提供了QLocalServer
和QLocalSocket类,允许在本地设置中进行类似网络的通信。【例12.2】编写一个Qt应用程序,通过LocalServer/Socket方法来实现进程之间的通信。(1)打开QtCreator集成开发环境,创建2个基于QDialog类的Qt应用程序。项目名称分别为examp12_1_server和examp12_1_client。前者表示服务器端程序,后者表示客户端程序。(2)编写服务器端程序代码。下面只给出部分关键代码,其他请参见教材源码。(3)编写客户端程序代码。下面只给出部分关键代码,其他请参见教材源码。(4)构建并运行程序。3、SharedMemory方法QtNetwork模块中的跨平台的QSharedMemory
共享内存类,提供对操作系统的共享内存的实现,它允许多个线程和进程安全访问共享内存段。QSystemSemaphore也可以用来控制访问由系统共享的资源以及进程之间的通信。【例12.3】编写一个Qt应用程序,使用共享内存来实现进程之间的通信。(1)打开QtCreator集成开发环境,创建一个基于QWidget类的Qt应用程序。项目名称为examp12_3。(2)双击项目视图中的widget.ui界面文件,打开QtDesigner设计工具,对程序主窗口界面进行设计。在主窗口中添加1个QLabel标签控件,和2个QPushButton类型的按钮。3个对象的名称分别为label、loadFromFileButton和loadFromSharedMemoryButton。(3)为2个QPushButton按钮添加clicked()信号的槽函数,函数名称分别为on_loadFromFileButton_clicked()和on_loadFromSharedMemoryButton_clicked()。(4)打开widget.h头文件,为Widget类添加一个名为detach()的私有成员函数,用于将进程与共享内存段分离;添加一个名为sharedMemory的QSharedMemory私有成员对象,用于表示共享内存段。代码如下。private:voiddetach();private:…QSharedMemorysharedMemory;(5)打开widget.cpp文件,编写构造函数、自定义函数,以及槽函数代码,以实现程序功能。如下所示。(4)构建并运行程序。4、D-Bus协议方法Qt的D-Bus模块是一种可用于使用D-Bus协议实现IPC的唯一Unix库。它将Qt的信号和槽机制延伸到IPC级别,允许由一个进程发出的信号被连接到另一个进程的槽。该方法的实现,请参见Qt的示例程序D-BusChatExample和D-BusRemoteControlledCarExample5、QProcess方法跨平台类QProcess能够用于启动外部程序作为子进程,并与它们进行通讯。它提供了用于监测和控制该子进程状态的API。另外,QProcess为从QIODevice继承的子进程提供了输入/输出通道。该方法的实现,请参见上面12.1.1小节中的code_12_1_1示例项目。6、SessionManagement方法在Linux/X11平台上,Qt提供了会话管理的支持。会话容许事件传播到进程,例如,当检测到关机时,进程和应用程序能够执行任何必须的操作,如保存打开的文档等。12.3线程Qt对线程的支持是通过三个方面来实现的:一是提供了一组与平台无关的线程类,二是提供了一个线程安全的事件发送方式,三是提供了跨线程的信号与槽的关联。Qt对多线程操作的全面支持,使得开发可移植的Qt多线程应用程序变得非常容易,同时还可以充分发挥多处理器中各个内核的效用。12.3.1线程的运行在Qt的多线程应用程序中,通常使用QThread类提供的方法来对线程进行管理。一个QThread类的对象管理一个线程,默认情况下,线程是在QThread::run()函数中开始运行的,run()函数通过调用exec()启动并运行Qt的事件循环。1、线程的创建在多线程编程中,将应用程序的线程称为主线程,额外创建的线程称为工作线程。工作线程可以通过两种方法来创建:一种方法是自定义QThread类的子类,并重载run()函数;另一种方法是先创建工作对象,然后使用QObject::moveToThread()函数将工作对象嵌入到线程中。(1)使用QThread子类对象通过子类化QThread来创建工作线程,是Qt多线程编程中的常用方法。下面是一段示例代码。(2)使用QObject::moveToThread()函数通过这种方法创建工作线程,首先需要创建一个工作者对象,将线程任务集中到这个对象中,然后使用QObject::moveToThread()函数完成工作线程的创建。示例如下。2、线程的启动工作线程创建完成后,可以在外部创建该线程的实例,然后调用start()函数来开始执行该线程,start()默认会调用run()函数。下面来看一个简单的实例。【例12.4】编写一个Qt应用程序,统计n个自然数中质数的个数。要求统计计算在单独的线程中完成,主线程接收用户输入并显示统计结果。(1)打开QtCreator集成开发环境,创建一个基于QWidget类的Qt应用程序。项目名称为examp12_4。(2)双击项目视图中的widget.ui界面文件,打开QtDesigner设计工具,对程序主窗口界面进行设计。在主窗口中添加2个QLabel标签、1个QPushButton按钮、1个QLineEdit单行文本输入框和1个QPlainTextEdit多行文本编辑器。其中,单行文本输入框、按钮和多行文本编辑器控件对象的名称分别为lineEdit、pushButton和plainTextEdit。(3)右击主窗口中的lineEdit控件,选择快捷菜单中的“Gotoslots…”菜单命令,为单行文本控件添加editingFinished信号关联槽函数on_lineEdit_editingFinished();使用同样的方法,为“计算”按钮控件添加clicked信号关联槽函数on_pushButton_clicked()。(4)在项目中添加一个QThread类的派生类MyThread,并重载run()虚函数。为类MyThread添加私有成员变量endNum,用于存储需要统计的自然数的个数;为endNum成员变量添加公有的设置函数setEndNum()。代码如下所示。接着,编写mythread.cpp文件中的代码,完成成员变量的初始化、成员变量的设置和线程任务等工作。代码如下所示。(5)打开widget.h文件,在类Widget中添加一个MyThread类型的对象myThread,并为上述第(3)步中创建的2个槽函数添加代码,实现相应的功能。如下所示。(6)构建并运行程序。程序运行后,输入不同的n值,并单击“计算”按钮进行测试,结果如图12.10所示。该程序子线程中的计算结果是直接在控制台输出的,如果要将计算结果传递到主线程中,就需要了解线程间通信的基本方法。12.3.2线程间通信线程间的通信一般通过两种方法来实现,即成员变量方法和自定义信号方法。成员变量方法就是通过线程对象的成员变量来返回线程数据;自定义信号方法通过在线程类中定义信号,利用信号参数来传递线程数据。1、成员变量方法由于线程任务是在run()函数中完成的,而run()函数又属于线程类的成员函数,所以可以通过线程类的成员变量来存储run()函数中的相关数据。【例12.5】编写一个Qt应用程序,使用成员变量来实现线程之间的通信。(1)复制例12.4中的项目examp12_4,并将名称修改为examp12_5。(2)打开项目中的mythread.h文件,在MyThread线程类中添加一个类型为long的私有成员变量result,并为其添加公有的getResult()函数。getResult()函数实现代码如下:longMyThread::getResult(){returnresult;}(3)修改run()函数中的代码,将计算结果赋值给成员变量result。如下所示。voidMyThread::run(){…//qDebug()<<"在1~"<<endNum<<"的n个自然数中,质数的个数为:"<<n;result=n;}(4)打开项目中的widget.h文件,在类Widget中添加槽函数returnResult()。其实现代码如下所示:voidWidget::returnResult(){longr=myThread.getResult();QStringstr;str.setNum(r);ui->plainTextEdit->insertPlainText(str);}(5)在Widget类的构造函数中编写代码,将槽函数returnResult()和QThread::finished信号关联。代码如下:connect(&myThread,&QThread::finished,this,&Widget::returnResult);子线程运行结束后,即刻调用主窗口中的returnResult()槽函数,将计算结果显示在多行文本编辑器光标所在的位置。(6)构建并运行程序。程序运行后,在文本输入框中输入n并回车,然后单击“计算”按钮开始统计计算。程序计算时,可以在主窗口中进行其他操作,主线程没有被阻塞。如图12.12所示。2、自定义信号方法在Qt的信号与槽通讯机制中,对象在发射信号的时候是可以附带传送一些参数的。所以,可以通过在线程类中定义信号,利用信号参数来传递线程数据。【例12.6】编写一个Qt应用程序,使用自定义信号方法实现线程之间的通信。(1)复制例12.4中的项目examp12_4,并将名称修改为examp12_6。(2)打开项目中的mythread.h头文件,为线程类MyThread添加一个信号函数。代码如下:signals:voidreturnResult(longresult);(3)打开项目中的mythread.cpp文件,修改线程类MyThread的run()函数中的代码,如下所示。voidMyThread::run(){…//qDebug()<<"在1~"<<endNum<<"的n个自然数中,质数的个数为:"<<n;emitreturnResult(n);}(4)打开项目文件widget.h,在类Widget中添加槽函数getResult(),并编写其实现代码。如下所示。voidWidget::getResult(longresult){longr=result;QStringstr;str.setNum(r);ui->plainTextEdit->insertPlainText(str);}(5)在Widget类的构造函数中编写代码,将槽函数getResult()和MyThread::returnResult信号关联。代码如下:connect(&myThread,&MyThread::returnResult,this,&Widget::getResult);(6)构建并运行程序。12.4线程控制线程之间存在着互相制约的关系,具体可以分为互斥和同步这两种关系。在Qt中,线程的互斥与同步控制,可以使用QMutex、QMutexLocker、QReadWriteLock、QReadLocker、QWriteLocker、Qsemaphore和QWaitCondition
等类来实现。12.4.1基于互斥量互斥量可以通过Qmutex
或QMutexLocker类实现。Qmutex和QMutexLocker
又称为互斥锁,用于保护共享资源(如对象、数据结构和代码段等),它们能够保证多线程程序中在同一时刻只有一个线程访问共享资源。【例12.7】编写一个Qt应用程序,示例使用互斥量保护共享资源。(1)打开QtCreator集成开发环境,创建一个基于QWidget类的Qt应用程序,项目名称为examp12_7。(2)在项目中添加一个名为TestData的C++类,并在该类中定义2个静态成员sharedNumber和sharedNumMutex,前者表示共享整型数据;后者表示互斥锁。代码如下。(3)在项目中添加2个QThread的子线程类,类名分别为WorkThread1和WorkThread2。在这两个类中实现QThread::run()虚函数,代码如下。(4)在项目主窗口中添加一个QPushButton类型的按钮,并在其clicked()信号对应的槽函数中编写代码,如下所示。voidWidget::on_pushButton_clicked(){m_workThread2.start();//先启动线程2m_workThread1.start();}(5)构建并运行程序。为了对比运行结果,程序运行测试分两次来进行。先注释掉上述(3)步代码中的语句1、语句5和语句9,测试不使用互斥锁的情形,结果如图12.14所示。从输出结果可以看出,在线程1中输出的结果为17(0+2+20-5),这个结果是执行了语句6、语句2和语句3后得到的。也就是说,在线程2还没有对共享数据TestData::sharedNumber修改(语句7还没有执行)完成的时候,线程1便对该共享数据进行了修改。很显然这个计算结果是不符合程序设计者的初衷的。接着,测试使用互斥锁后程序的运行情况。取消语句1、语句5和语句9的注释,重新构建并运行程序,结果如图12.15所示。从结果可以看到,使用互斥锁以后,线程1就不能在线程2访问共享数据的时候对其进行操作了。这样有效保护了程序中的共享资源。12.4.2基于信号量信号量QSemaphore可以理解为对互斥量QMutex功能的扩展,互斥量只能锁定一次而信号量可以获取多次,它可以用来保护一定数量的同种资源。QSemaphore类的成员函数及功能如表12.4所示。信号量的典型用例是控制生产者/消费者之间共享的环形缓冲区。下面是一个简单的停车场车位资源分配实例。【例12.8】编写一个Qt应用程序,示例信号量QSemaphore的使用方法。(1)打开QtCreator集成开发环境,创建一个基于QWidget类的Qt应用程序,项目名称为examp12_8。(2)在项目中添加一个名为TestData的C++类,并在该类中定义2个静态成员gData和gSemaphore,前者表示共享资源;后者表示信号量。代码如下所示。(3)在项目中添加一个QThread的子线程类,类名为MyThread。实现MyThread类的QThread::run()虚函数,并编写代码。如下所示。(4)在项目主窗口中添加一个QPushButton类型的按钮,并在其clicked()信号对应的槽函数中编写代码,如下所示。(5)构建并运行程序。12.4.3基于QReadWriteLock在基于互斥量Qmutex的线程控制中,每次只能有一个线程获得互斥量的权限。如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队。实际上,若只是读取一个变量,是可以让多个线程同时访问的。在这种只读取的情况下,若仍然使用互斥量就会降低程序的性能。针对上述问题,Qt提供了一个读写锁QReadWriteLock。读写锁是基于读或写的模式进行代码段锁定的,在多个线程读写一个共享资源时,可以解决使用互斥量影响程序性能这个问题。与互斥量Qmutex相比较,QReadWriteLock的特点是:读共享,写独占;且默认写锁优先级高于读锁。【例12.9】编写一个Qt应用程序,示例QReadWriteL
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 陪诊护理员患者安全与风险防范
- 碳13呼气试验的样本处理
- 肝豆状核变性护理中的文化敏感性
- 审计三级复核制度规定
- 审计促进出台8个制度
- 农垦审计管理制度
- 审计局内审工作制度范本
- 审计法制投入保障制度
- 出纳员绩效考核制度
- 家纺专卖店绩效考核制度
- 癌症患者生活质量量表EORTC-QLQ-C30
- 消防工程施工消防工程施工方案和技术措施
- 实验室计量器器具校准操作规程
- 2024年湖南出版投资控股集团招聘笔试参考题库含答案解析
- DL∕T 547-2020 电力系统光纤通信运行管理规程
- 电气控制与PLC教案电气控制与PLC教案
- 建筑材料说课公开课一等奖市赛课获奖课件
- 湖南2023年长沙银行理财经理社会招聘(37)考试参考题库含答案详解
- 混凝土搅拌车维护保养
- 薄膜的物理气相沉积
- 铣刨加罩道路工程施工组织设计方案
评论
0/150
提交评论