该文章原创于Qter开源社区(www.qter.org),作者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类的问题(可以简单的通过这样的方式来解决:将一些非常耗时的操作放在一个单独的工作线程中来进行,等该工作线程完成后将结果返回给主线程,最后由主线程将结果显示到屏幕上)。大家应该谨慎的使用线程。

http://blog.csdn.net/an505479313/article/details/48494901

QT的进程与线程(cmd /c的写法)的更多相关文章

  1. Qt 进程和线程之一:运行一个进程和进程间通信

    Qt提供了对进程和线程的支持.本节讲述了怎样在Qt应用程序中启动一个进程,以及几种常用的进程间通信方法.如果对进程和线程的概念不是很了解,可以看我的另一篇博客:[多进程和多线程的概念. 设计应用程序时 ...

  2. Python之路,Day9, 进程、线程、协程篇

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  3. 进程和线程及Linux下的编程

    一.概述 进程和线程网络上有一堆解释,我不喜欢抄袭,也不喜欢套用太教科书的说法.就以我自己的理解来说说进程和线程吧,当然自己的理解肯定不是很严谨,但是理解起来应该会比教科书快一点.进程和线程都可以认为 ...

  4. (转)java中的进程与线程

    (转自地址http://www.ibm.com/developerworks/cn/java/j-lo-processthread/) Java 进程的建立方法 在 JDK 中,与进程有直接关系的类为 ...

  5. linux查看某个进程的线程id(spid)

    鉴于linux下线程的广泛使用 我们怎么查看某个进程拥有的线程id了 现在很多服务的设计 主进程->子进程->线程(比如mysql,varnish) 主进程负责侦听网络上的连接 并把连接发 ...

  6. php开启新的进程或者线程

    开启线程: $php_cli_path = '/usr/bin/php';$dir_path = dirname(__FILE__)."/".'CheckTaskState.she ...

  7. python 自动化之路 day 09 进程、线程、协程篇

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  8. 【转】Qt事件循环与线程 二

    转自:http://blog.csdn.net/changsheng230/article/details/6153449 续上文:http://blog.csdn.net/changsheng230 ...

  9. Java 多线程详解(二)------如何创建进程和线程

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html 在上一篇博客中,我们已经介绍了并发和并行的区别,以及进程和 ...

随机推荐

  1. Android 离线语音用法(讯飞语音)

    这次给大家带来的是项目的离线语音功能. 讯飞开放平台中的离线语音 首先创建开放平台的账号.这个不必多说 然后创建新应用 选择我的应用,例如以下图,注意下我打马赛克的地方,这个appId非常重要 点击进 ...

  2. URL传递中文参数,大坑一枚,Windows与Linux效果竟然不一致(两种解决方法)

    下午,计划2个小时搞定,个人官网第6次升级,就可以干点轻松的事了,结果,下午多搞了2个小时,晚上又搞了2个小时,才搞定. 最后一个世界难题是,URL传递中文参数. 问题大致是这么出现的:我为" ...

  3. 轮询方法FileSystemWatcher

    具体实现 FileSystemWatcher watcher = new FileSystemWatcher(WriteBackDir, "*result.txt"); watch ...

  4. 【物理】概念的理解 —— Phase(相位)

    Phase is the position of a point in time (an instant) on a waveform cycle. 相位指的是波形周期中点在某一时刻的位置.Phase ...

  5. 极光推送Jpush功能(具体参照官网说明文档,注意此文红色字体)

    1.导入框架 2. //推送 #import "APService.h" - (BOOL)application:(UIApplication *)application didF ...

  6. 关于用什么作为dll版本号的思考

    作者:朱金灿 来源:http://blog.csdn.net/clever101 一个软件模块的版本如何维护呢?毫无疑问,它需要一个版本号.通过比对版本号就知道哪个高版本,哪个是低版本了.软件模块以d ...

  7. [GeekBand] C++ 基础知识之 The Big Three

    本文是GeekBand课程体系中,侯捷老师讲课内容的部分内容总结. 参考书籍如下:Effitive C++ C++ Primer 第五版 http://blog.csdn.net/lwbeyond/a ...

  8. hadoop 3.x 集群/单个节点的启动与停止

    1.单个节点操作 启动|停止单个节点 hdfs --daemon start|stop datanode hdfs --daemon start|stop namenode 启动|停止单个节点的Nod ...

  9. 辨异 —— 行星 vs 恒星

    star:恒星,planet:行星: 1. 恒星 恒星是指宇宙中靠核聚变产生的能量而自身能发热发光的星体(比如太阳).过去天文学家以为恒星的位置是永恒不变的,以此为名.但事实上,恒星也会按照一定的轨迹 ...

  10. 数字电路基本概念 —— fan-in/fan-out

    0. 从模拟电路到数字电路 数字电路抗干扰能力强: 模拟电路会随着信号的传输而放大,这是因为模拟电路中信号几乎完全将真实信号按比例表现为电压或者电流的形式: 模拟电路是数字电路的基础 74LS283 ...