[Qt教程] 第39篇 网络(九)进程和线程

简介:
楼主
  发表于 2013-8-29 15:48:56  | 查看: 415 | 回复: 0
进程和线程
版权声明

该文章原创于作者yafeilinux,转载请注明出处!


导语

在前面的几节内容中讲解了Qt网络编程的一些基本内容,这一节来看一下在Qt中进程和线程的基本应用。

环境:Windows Xp + Qt 4.8.5+Qt Creator2.8.0



目录

一、进程
二、线程



正文


一、进程
    在设计一个应用程序时,有时不希望将一个不太相关的功能集成到程序中,或者是因为该功能与当前设计的应用程序联系不大,或者是因为该功能已经可以使用现成的程序很好的实现了,这时就可以在当前的应用程序中调用外部的程序来实现该功能,这就会使用到进程。Qt应用程序可以很容易的启动一个外部应用程序,而且Qt也提供了在多种进程间通信的方法。
    Qt的QProcess类用来启动一个外部程序并与其进行通信。下面我们来看一下怎么在Qt代码中启动一个进程。

1.首先创建QtGui应用。
工程名称为“myProcess”,其他选项保持默认即可。

2.然后设计界面。
在设计模式往界面上拖入一个Push Button部件,修改其显示文本为“启动一个进程”。

3.修改槽。
在按钮上点击鼠标右键,转到其clicked()信号对应的槽,更改如下:
void MainWindow::on_pushButton_clicked()
{
     myProcess.start("notepad.exe");
}

4.进入mainwindow.h文件添加代码。
先添加头文件包含:#include <QProcess>,然后添加私有对象定义:QProcess myProcess;

5.运行程序。
当单击界面上的按钮时就会弹出一个记事本程序。

这里我们使用QProcess对象运行了Windows系统下的记事本程序(即notepad.exe程序),因为该程序在系统目录中,所以这里不需要指定其路径。大家也可以运行其他任何的程序,只需要指定其具体路径即可。我们看到,可以使用start()来启动一个程序,有时启动一个程序时需要指定启动参数,这种情况在命令行启动程序时是很常见的,下面来看一个例子,还在前面的例子的基础上进行更改。

1.在mainwindow.h文件中添加代码。
添加私有槽:
private slots:
    void showResult();

2.在mainwindow.cpp文件中添加代码。

(1)先添加头文件包含:#include <QDebug>,然后在构造函数中添加如下代码:
connect(&myProcess,SIGNAL(readyRead()), this, SLOT(showResult()));

(2)然后添加showResult()槽的定义:
void MainWindow::showResult()
{
    qDebug() << "showResult: " << endl
            << QString(myProcess.readAll());
}

(3)最后将前面按钮的单击信号对应的槽更改为:
void MainWindow::on_pushButton_clicked()
{
    QString program = "cmd.exe";
    QStringList arguments;
    arguments << "/c dir&pause";
    myProcess.start(program, arguments);
}
    这里在启动Windows下的命令行提示符程序cmd.exe时为其提供了命令作为参数,这样可以直接执行该命令。当命令执行完以后可以执行showResult()槽来显示运行的结果。这里为了可以显示结果中的中文字符,使用了QString()进行编码转换。这需要在mian()函数中添加代码。

3. 为了确保可以显示输出的中文字符,在main.cpp文件中添加代码。
先添加头文件包含#include <QTextCodec>,然后在main()函数第一行代码下面,添加如下一行代码:
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());

4.运行程序。
按下界面上的按钮,会在Qt Creator中的应用程序输出栏中输出命令的执行结果。
    对于Qt中进程进一步的使用可以参考QProcess类的帮助文档。在Qt中还提供了多种进程间通信的方法,大家可以在Qt帮助中查看Inter-ProcessCommunication in Qt关键字对应的文档。


二、线程
   
Qt提供了对线程的支持,这包括一组与平台无关的线程类,一个线程安全的发送事件的方式,以及跨线程的信号-槽的关联。这些使得可以很容易的开发可移植的多线程Qt应用程序,可以充分利用多处理器的机器。多线程编程也可以有效的解决在不冻结一个应用程序的用户界面的情况下执行一个耗时的操作的问题。关于线程的内容,大家可以在Qt帮助中参考Thread Support in Qt关键字。

(一)启动一个线程
    Qt中的QThread类提供了平台无关的线程。一个QThread代表了一个在应用程序中可以独立控制的线程,它与进程中的其他线程分享数据,但是是独立执行的。相对于一般的程序都是从main()函数开始执行,QThread从run()函数开始执行。默认的,run()通过调用exec()来开启事件循环。要创建一个线程,需要子类化QThread并且重新实现run()函数。
    每一个线程可以有自己的事件循环,可以通过调用exec()函数来启动事件循环,可以通过调用exit()或者quit()来停止事件循环。在一个线程中拥有一个事件循环,可以使它能够关联其他线程中的信号到本线程的槽上,这使用了队列关联机制,就是在使用connect()函数进行信号和槽的关联时,将Qt::ConnectionType类型的参数指定为Qt::QueuedConnection。拥有事件循环还可以使该线程能过使用需要事件循环的类,比如QTimer和QTcpSocket类等。注意,在线程中是无法使用任何的部件类的。
    下面来看一个在图形界面程序中启动一个线程的例子,在界面上有两个按钮,一个用于开启一个线程,一个用于关闭该线程。

1.创建项目。
    新建Qt Gui应用,名称为“myThread”,类名为“Dialog”,基类选择QDialog。

2.设计界面。
    完成项目创建后进入设计模式,向界面中放入两个Push Button按钮,将第一个按钮的显示文本更改为“启动线程”,将其objectName属性更改为startButton;将第二个按钮的显示文本更改为“终止线程”,将其objectName属性更改为stopButton,将其enabled属性取消选中。

3.添加自定义线程类。
    向项目中添加新的C++类,类名设置为“MyThread”,基类设置为“QThread”,类型信息选择“继承自QObject”。完成后进入mythread.h文件,先添加一个公有函数声明:
void stop();
然后再添加一个函数声明和一个变量的定义:
protected:
    void run();
private:
    volatile bool stopped;
    这里stopped变量使用了volatile关键字,这样可以使它在任何时候都保持最新的值,从而可以避免在多个线程中访问它时出错。然后进入mythread.cpp文件中,先添加头文件#include <QDebug>,然后在构造函数中添加如下代码:
stopped = false;
    这里将stopped变量初始化为false。下面添加run()函数的定义:
void MyThread::run()
{
    qreal i = 0;
    while (!stopped)
        qDebug() << QString("in MyThread: %1").arg(i++);
    stopped = false;
}
    这里一直判断stopped变量的值,只要它为false,那么就一直打印字符串。下面添加stop()函数的定义:
void MyThread::stop()
{
    stopped = true;
}
    在stop()函数中将stopped变量设置为了true,这样便可以结束run()函数中的循环,从而从run()函数中退出,这样整个线程也就结束了。这里使用了stopped变量来实现了进程的终止,并没有使用危险的terminate()函数。

4.在Dialog类中使用自定义的线程。
先到dialog.h文件中,添加头文件包含:
#include "mythread.h"
    然后添加私有对象的定义:
MyThread thread;
   
下面到设计模式,分别进入两个按钮的单击信号对应的槽,更改如下:

// 启动线程按钮
void Dialog::on_startButton_clicked()
{
    thread.start();
    ui->startButton->setEnabled(false);
    ui->stopButton->setEnabled(true);
}

// 终止线程按钮
void Dialog::on_stopButton_clicked()
{
    if (thread.isRunning()) {
        thread.stop();
        ui->startButton->setEnabled(true);
        ui->stopButton->setEnabled(false);
    }
}
    在启动线程时调用了start()函数,然后设置了两个按钮的状态。在终止线程时,先使用isRunning()来判断线程是否在运行,如果是,则调用stop()函数来终止线程,并且更改两个按钮的状态。现在运行程序,按下“启动线程”按钮,查看应用程序输出栏的输出,然后再按下“终止线程”按钮,可以看到已经停止输出了。
    下面我们接着来优化这个程序,通过信号和槽来将子线程中的字符串显示到主界面上。

1.在mythread.h文件中添加信号的定义:
signals:
void stringChanged(const QString &);

2.然后到mythread.cpp文件中更改run()函数的定义:
void MyThread::run()
{
    long int i = 0;
    while (!stopped) {
       QString str = QString("in MyThread: %1").arg(i);
       emit stringChanged(str);
       msleep(1000);
       i++;
    }
    stopped = false;
}
这里每隔1秒就发射一次信号,里面包含了生成的字符串。

3.到dialog.h文件中添加槽声明:
private slots:
    void changeString(const QString &);

4.打开dialog.ui,然后向主界面上拖入一个Label标签部件。

5.到dialog.cpp文件中,在构造函数里面添加信号和槽的关联
// 关联线程中的信号和本类中的槽
connect(&thread, SIGNAL(stringChanged(QString)),
this, SLOT(changeString(QString)));

6.然后添加槽的定义:
void Dialog::changeString(const QString &str)
{
    ui->label->setText(str);
}
    这里就是将子线程发送过来的字符串显示到主界面上。现在可以运行程序,查看效果了。


(二)线程同步
    Qt中的QMutex、QReadWriteLock、QSemaphore和QWaitCondition类提供了同步线程的方法。虽然使用线程的思想是多个线程可以尽可能的并发执行,但是总有一些时刻,一些线程必须停止来等待其他线程。例如,如果两个线程尝试同时访问相同的全局变量,结果通常是不确定的。QMutex提供了一个互斥锁(mutex);QReadWriteLock即读-写锁;QSemaphore即信号量;QWaitCondition即条件变量。

(三)可重入与线程安全
在查看Qt的帮助文档时,在很多类的开始都写着“All functions in this class are reentrant,或者“All functions in this class are thread-safe。在Qt文档中,术语“可重入(reentrant)和“线程安全(thread-safe)用来标记类和函数,来表明怎样在多线程应用程序中使用它们:
  • 一个线程安全的函数可以同时被多个线程调用,即便是这些调用使用了共享数据。因为该共享数据的所有实例都被序列化了。
  • 一个可重入的函数也可以同时被多个线程调用,但是只能是在每个调用使用自己的数据时。

结语


最后要注意的是,使用线程是很容易出现问题的,比如无法在主线程以外的线程中使用GUI类的问题(可以简单的通过这样的方式来解决:将一些非常耗时的操作放在一个单独的工作线程中来进行,等该工作线程完成后将结果返回给主线程,最后由主线程将结果显示到屏幕上)。大家应该谨慎的使用线程。



涉及到的源码下载:  39.rar (8.31 KB, 下载次数: 7) 
相关文章
|
2天前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
2月前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
15天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
65 20
|
1天前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
11 0
|
1月前
|
消息中间件 调度
如何区分进程、线程和协程?看这篇就够了!
本课程主要探讨操作系统中的进程、线程和协程的区别。进程是资源分配的基本单位,具有独立性和隔离性;线程是CPU调度的基本单位,轻量且共享资源,适合并发执行;协程更轻量,由程序自身调度,适合I/O密集型任务。通过学习这些概念,可以更好地理解和应用它们,以实现最优的性能和资源利用。
66 11
|
1月前
|
前端开发 小程序 Java
uniapp-网络数据请求全教程
这篇文档介绍了如何在uni-app项目中使用第三方包发起网络请求
61 3
|
1月前
|
Java Linux 调度
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
42 6
|
2月前
|
消息中间件 Unix Linux
【C语言】进程和线程详解
在现代操作系统中,进程和线程是实现并发执行的两种主要方式。理解它们的区别和各自的应用场景对于编写高效的并发程序至关重要。
95 6
|
2月前
|
调度 开发者
深入理解:进程与线程的本质差异
在操作系统和计算机编程领域,进程和线程是两个核心概念。它们在程序执行和资源管理中扮演着至关重要的角色。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
90 5
|
2月前
|
算法 调度 开发者
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。

热门文章

最新文章