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

Qt提供了一个与平台无关的QProcess类,用以对进程的支持。本节讲述了怎样在Qt应用程序中启动一个外部程序进程,以及几种常用的进程间通信方法。

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

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

1、运行一个进程

Qt的QProcess类可以用来启动一个外部程序并与其进行通信。下面我们来看一下怎么在Qt代码中启动一个进程。

运行一个进程打开记事本

首先创建QtGui应用,工程名称为“myProcess”,其他选项保持默认即可。先进入mainwindow.h文件添加代码,添加私有对象定义:QProcess myProcess。然后在设计模式往界面上拖入一个Push Button部件,修改其显示文本为“启动一个进程”。在按钮上点击鼠标右键,转到其clicked()信号对应的槽,更改如下:

void MainWindow::on_pushButton_clicked()
{
     myProcess.start("notepad.exe");
}

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

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

QProcess也提供了一组函数,可以脱离事件循环来使用,它们会挂起调用的线程直到确定的信号被发射:

  • waitForStarted()阻塞直到进程已经启动;
  • waitForReadyRead()阻塞直到在当前读通道上有可读的数据;
  • waitForBytesWritten()阻塞直到一个有效负载数据已经被写人到进程;
  • waitForFinished()阻塞直到进程已经结束。

2、进程间通信方式

Qt提供了多种方法在Qt应用程序中实现进程间通信IPC(Inter-Process Commu­nication)。 简单介绍如下 :

TCP/IP

跨平台的Qt Network模块提供的类可以让网络编程更加便携和方便。它提供了高级类(例如:QNetworkAccessManager、QFtp)通信,使用特定的应用程序级协议,和较底层的类(例如:QTcpSocket、QTcpServer、QSslSocket)用于实现协议。

共享内存

跨平台的QSharedMemory-共享内存类,提供对操作系统的共享内存的实现。它允许多个线程和进程安全访问共享内存段。此外,QSystemSemaphore可以用来控制访问由系统共享的资源,以及进程之间的通信。

D-Bus

Qt的D-Bus模块是一种可用于使用D-Bus协议实现IPC的唯一Unix库。它将Qt的信号和槽机制延伸到IPC级别,允许由一个进程发出的信号被连接到另一个进程的槽。Qt的D-Bus文档已经详细说明如何使用Qt中的D-Bus模块。

QProcess

使用先前提到的QProcess类。跨平台类QProcess可以用于启动外部程序作为子进程,并与它们进行通信。它提供了用于监测和控制该子进程状态的API。另外,QProcess为从QIODevice继承的子进程提供了输入/输出通道。

会话管理

在Linux/X11平台上,Qt提供了会话管理的支持。会话允许事件传播到进程,例如,当检测到关机时。进程和应用程序可以执行任何必要的操作,例如:保存打开的文档。

3、共享内存之进程间通信实例

下面来看一个使用共享内存的例子,它实现的功能是:先在一个对话框中将一张图片写入到共享内存段中,然后再在另一个对话框中从共享内存段读出该图片。

新建Qt Gui应用,名称为mylPC,类名为Dialog,基类选择QDialog。完成后进人设计模式,向界面中放入两个Push Button部件和一个Label部件。将一个按钮的显示文本更改为“从文件中加载图片”,将其objectName属性更改为loadFromFileButton,将另一个按钮的显示文本更改为“从共享内存显示图片”,将其objectName属性更改为loadFromSharedMemoryButton。然后进人dialog.h文件,修改如下:

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QSharedMemory>

namespace Ui {
    class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

public slots:
    //从文件中加载图片到共享内存
    void loadFromFile();

    //从共享内存中加载图片
    void loadFromMemory();

private slots:
    //从文件中加载图片按钮
    void on_loadFromFileButton_clicked();

    //从共享内存加载并显示图片按钮
    void on_loadFromSharedMemoryButton_clicked();

private:
    //将进程与共享内存段进行分离,如果失败则进行提示
    void detach();

private:
    Ui::Dialog *ui;

    QSharedMemory sharedMemory; //定义一个共享内存对象
};

#endif // DIALOG_H

然后进人dialog.cpp文件,修改如下:

#include "dialog.h"
#include "ui_dialog.h"

#include <QFileDialog>
#include <QBuffer>
#include <QDebug>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    //在使用共享内存以前,需要设置key,系统用它作为底层共享内存段的标识
    sharedMemory.setKey("QSharedMemoryExample");
}

Dialog::~Dialog()
{
    delete ui;
}

//从文件中加载图片到共享内存
void Dialog::loadFromFile()
{
    //判断该进程是否已经连接到共享内存段,连接成功返回true
    if (sharedMemory.isAttached())
        detach();

    ui->label->setText(tr("选择一个图片文件!"));
    //使用文件对话框获得打开图片路径
    QString fileName = QFileDialog::getOpenFileName(nullptr, QString(), QString(),
                                                    tr("Images (*.png *.jpg)"));

    //label标签显示图片
    QImage image;
    if (!image.load(fileName))
    {
        ui->label->setText(tr("选择的文件不是图片,请选择图片文件!"));
        return;
    }
    ui->label->setPixmap(QPixmap::fromImage(image));

    //将图片加载到共享内存
    //使用QBuffer来暂存图片,这样便可以获得图片的大小,还可以获得图片数据的指针
    QBuffer buffer;
    buffer.open(QBuffer::ReadWrite);
    QDataStream out(&buffer);
    out << image;
    int size = static_cast<int>(buffer.size());
    //使用create()函数创建指定大小的共享内存段,该函数还会自动将共享内存段连接到本进程上。
    if (!sharedMemory.create(size))
    {
        ui->label->setText(tr("无法创建共享内存段!"));
        return;
    }

    //在进行共享内存段的操作前,需要先进行加锁
    sharedMemory.lock();
    char *to = static_cast<char*>(sharedMemory.data());
    const char *from = buffer.data().data();
    //使用memcpy()函数将图片数据复制到共享内存
    memcpy(to, from, static_cast<size_t>(qMin(sharedMemory.size(), size)));
    //等操作完成后,再进行解锁。
    sharedMemory.unlock();
}

//从共享内存中加载图片
void Dialog::loadFromMemory()
{
    //使用attache()函数将进程连接到共享内存段
    if (!sharedMemory.attach())
    {
        ui->label->setText(tr("无法连接到共享内存段,\n"
                              "请先加载一张图片!"));
        return;
    }

    QBuffer buffer;
    QDataStream in(&buffer);
    QImage image;

    //使用QBuffer来读取共享内存段中的数据
    sharedMemory.lock();
    buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
    buffer.open(QBuffer::ReadOnly);
    in >> image;
    sharedMemory.unlock();

    //将进程与共享内存段进行分离,如果失败则进行提示
    sharedMemory.detach();
    ui->label->setPixmap(QPixmap::fromImage(image));
}

//将进程与共享内存段进行分离,如果失败则进行提示
void Dialog::detach()
{
    if (!sharedMemory.detach())
        ui->label->setText(tr("无法从共享内存中分离!"));
}

//从文件中加载图片按钮
void Dialog::on_loadFromFileButton_clicked()
{
    loadFromFile();
}

//从共享内存加载并显示图片按钮
void Dialog::on_loadFromSharedMemoryButton_clicked()
{
    loadFromMemory();
}

在一个运行的实例上单击“从文件中加载图片”按钮,然后选择一张图片。在第二个运行的实例上单击“从共享内存显示图片”按 钮,这时便会显示第一个实例中加载的图片,效果如下所示:

 

4、程序分析

(1)loadFromFile()

  • 这里先使用isAttached()函数判断该进程是否已经连接到共享内存段,如果是,那么就调用detach()先将该进程与共享内存段进行分离。
  • 然后使用QFileDialog类来打开一个图片文件,并将其显示到标签上。为了将图片加载到共享内存,这里使用了QBuffer来暂存图片,这样便可以获得图片的大小,还可以获得图片数据的指针。
  • 后面使用了create()函数来创建指定大小的共享内存段,其大小的单位是字节,该函数还会自动将共享内存段连接到本进程上。
  • 在操作共享内存段时要使用lock()进行加锁,然后才可以使用memcpy()函数将buffer对应的数据段复制到共享内存段,操作完成后要使用unlock()进行解锁。这样在同一时间,就只能有一个进程允许操作共享内存段了。

(2)loadFromMemory()

  • 这里先使用attache()函数将进程连接到共享内存段。
  • 在操作共享内存段时要使用lock()进行加锁,然后才可以使用QBuffer来读取共享内存段中的数据,操作完成后要使用unlock()进行解锁。
  • 因为现在已经不需要使用共享内存了,所以调用detach()函数将进程与共享内存段进行分离。最后将图片显示到标签中。

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Qt进程通信和同步是指在Qt框架下,不同进程进行信息交流和数据同步的过程。Qt提供了多种机制来实现进程通信和同步,包括信号槽、共享内存和套接字等。 首先,Qt的信号槽机制可以实现不同进程通信。通过使用信号和槽,一个进程可以发射信号,而其他进程可以接收这个信号并执行相应的槽函数。这种机制实现了进程的异步通信,允许不同进程进行消息的传递和处理。 其次,Qt还提供了共享内存机制来实现进程的数据共享和同步。使用共享内存,不同进程可以访问相同的内存空,从而实现数据的共享。多个进程可以通过读取和写入共享内存来交换数据,并且可以使用信号量等同步机制来确保数据的一致性和同步。 此外,Qt还支持套接字通信来实现进程的网络通信。通过创建套接字,不同进程可以建立起网络连接,进行数据的发送和接收。这种机制可以用于不同机器之进程通信,具有较高的灵活性和扩展性。 总之,Qt提供了多种进程通信和同步的机制,使得不同进程可以进行有效的信息交流和数据共享。这些机制可以根据具体的应用场景来选择和使用,以满足进程通信和同步的需求。 ### 回答2: Qt提供了多种方式来实现进程通信和同步。 第一种方式是使用信号与槽机制。Qt的信号与槽机制可以实现不同线程和进程通信。一个进程可以发射一个信号,而另一个进程则可以将其连接到一个槽函数来接收该信号。通过信号和槽机制,可以在进程进行异步通信,实现数据的传递和同步。 第二种方式是使用共享内存。Qt提供了QSharedMemory类,可以用于在多个进程共享内存区域。通过将数据存储在共享内存中,不同进程可以访问并修改这些数据,实现进程通信和同步。使用共享内存可以实现高效的数据传递,但需要注意在访问共享内存时进行互斥和同步操作,以避免数据竞争和不一致性。 第三种方式是使用Qt的网络模块。Qt提供了丰富的网络类,可以用于进程通信。可以使用QTcpSocket和QTcpServer类实现基于TCP的进程通信,也可以使用QUdpSocket类实现基于UDP的通信。通过网络模块,可以在不同的进程传递数据,并进行同步操作。 总的来说,Qt提供了多种灵活的方式来实现进程通信和同步。开发者可以根据实际需求来选择合适的方式,并结合Qt的其他功能来实现进程的高效交互。 ### 回答3: Qt提供了多种进程通信和同步的机制,如信号槽、共享内存和跨进程信号槽。 首先,Qt中的信号槽机制可以用于进程通信和同步。通过信号槽机制,一个进程可以发送信号,而另一个进程可以通过连接相应的槽函数来接收这些信号。这样,不同进程就可以进行通信和同步,实现进程数据的传递和事件的处理。 其次,Qt还提供了共享内存的机制。共享内存是一块特殊的内存区域,可以被多个进程同时访问。Qt中的QSharedMemory类可以方便地进行共享内存的创建、读写和释放。通过共享内存,进程可以共享数据,实现数据的共享和同步。 此外,Qt还提供了跨进程信号槽机制。通过QRemoteObject模块,可以将信号槽机制扩展到跨进程通信。在跨进程信号槽中,信号的发送和接收可以在不同的进程中,进程可以通过信号槽机制实现事件和数据的传递。 总之,Qt提供了多种进程通信和同步的机制,开发者可以根据实际需求选择合适的方式。通过这些机制,可以实现多个进程的数据传递和事件处理,提高程序的并发性和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值