当前位置: 首页 > article >正文

Qt中的QProcess与Boost.Interprocess:实现多进程编程

目录

QProcess简介

启动进程的不同方式

例子1:打开记事本程序

例子2:执行带有管道(|)的Linux命令

同步进程API

Boost.Interprocess简介

(一)共享内存:

(二)命名信号量与互斥锁示例

(三) 资源清理

实现多进程

使用QProcess管理多个进程

结合Boost.Interprocess实现进程间通信


QPocess是Qt提供的一个类,用于启动和管理外部进程。通过QPocess,开发者可以启动一个进程,与之通信(发送输入和读取输出),检查其状态,以及等待其完成。这在执行系统命令、运行其他程序或脚本时尤为有用。

Boost.Interprocess是Boost库中的一个模块,专门用于实现跨进程通信。它提供了多种机制,包括共享内存、命名信号量、互斥锁、消息队列等,允许不同进程之间高效地交换数据。

本文将详细介绍这两者的功能与用法,并展示如何在Qt项目中结合使用它们,实现高效的多进程编程。

QProcess简介

功能:

QPocess类用于启动和管理外部进程。

  • 启动外部程序:执行可执行文件,并传递命令行参数
  • 与外部进行通信:通过标准输入 输出 和 错误通道交换数据
  • 进程控制:终止 暂停 或 恢复进程,获取其退出状态
  • 同步与异步操作:支持阻塞和非阻塞的进程管理方式

用法

#include <QProcess>

// 创建QProcess对象
QProcess *process = new QProcess(this);

// 连接信号槽以读取输出
connect(process, &QProcess::readyReadStandardOutput, this, &YourClass::handleStdOutput);

// 启动外部程序
process->start("external_program", QStringList() << "arg1" << "arg2");

// 检查进程是否启动成功
if (!process->waitForStarted()) {
    qDebug() << "Process failed to start.";
}

信号与槽 QProcess通过信号和槽机制通知进程的各种状态和数据。常用的信号包括:

  • started():进程开始执行时发出。
  • readyReadStandardOutput():进程的标准输出有数据可读时发出。
  • readyReadStandardError():进程的标准错误输出有数据可读时发出。
  • finished(int exitCode, QProcess::ExitStatus exitStatus):进程结束时发出。
  • errorOccurred(QProcess::ProcessError error):发生错误时发出。
  • stateChanged(QProcess::ProcessState newState):进程状态改变时发出。

以下示例展示了如何连接这些信号:

QProcess process;
process->start("ls", QStringList() << "-l" << "/home/user");

// 连接信号以读取标准输出
connect(&process, &QProcess::readyReadStandardOutput, [&]() {
    qDebug() << "Standard output:" << process.readAllStandardOutput();
});

// 连接信号以读取标准错误
connect(&process, &QProcess::readyReadStandardError, [&]() {
    qDebug() << "Standard error:" << process.readAllStandardError();
});

// 连接信号以处理进程结束
connect(&process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
          [&](int exitCode, QProcess::ExitStatus exitStatus) {
    qDebug() << "Process finished with exit code" << exitCode << exitStatus;
});

启动进程的不同方式

QProcess提供了多种方式来启动外部进程,主要包括:

  1. start() 方法
    • 异步启动:启动进程后立即返回,不会阻塞调用线程。
    • 支持交互:可以与进程实时交互,如读取输出、写入输入。
    • 重定向:可以选择性地重定向标准输入输出。
QProcess process;
process.start("notepad.exe");
  1. execute() 方法
    • 同步启动:启动进程并阻塞调用线程,直到进程结束。
    • 简单使用:不适合需要与进程交互的场景。
    • 输出重定向:进程的输出会直接转发到调用进程的标准输出。
int exitCode = QProcess::execute("ls", QStringList() << "-l" << "/home/user");
qDebug() << "Process exited with code" << exitCode;
  1. startDetached() 方法
    • 分离启动:启动独立的进程,与父进程完全分离。
    • 独立运行:子进程不会随着父进程的退出而结束。
    • 不支持通信:无法通过信号槽机制与子进程通信。
bool started = QProcess::startDetached("notepad.exe", QStringList() << "C:/example.txt");
if (started) {
    qDebug() << "Notepad started successfully.";
}

例子1:打开记事本程序

点击按钮后启动记事本,并通过QProcess管理其生命周期。

mainwindow.h

#ifndef MAINWINDOW_H  
#define MAINWINDOW_H  

#include <QMainWindow>
#include <QProcess>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow  
{  
    Q_OBJECT  

        public:  
    MainWindow(QWidget *parent = nullptr);  
    ~MainWindow();  

    private slots:  
    void openProcess();  
    void readResult(int exitCode);  

    private:  
    Ui::MainWindow *ui;
    QProcess *p;  
};  

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"  
#include "ui_mainwindow.h"  
#include <QPushButton>
#include <QMessageBox>
#include <QTextCodec>
#include <QDebug>

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

    p = new QProcess(this);  

    QPushButton *bt = new QPushButton("Execute Notepad", this);  
    bt->setGeometry(QRect(QPoint(100, 100), QSize(200, 50)));
    connect(bt, &QPushButton::clicked, this, &MainWindow::openProcess);  
}  

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

void MainWindow::openProcess()  
{  
    p->start("notepad.exe");  
} 

// void MainWindow::readResult(int exitCode)  
// {  
//     if(exitCode == 0) {  
//         QTextCodec* gbkCodec = QTextCodec::codecForName("GBK");  
//         QString result = gbkCodec->toUnicode(p->readAll());  
//         QMessageBox::information(this, "Process Output", result);  
//     }  
// }

说明:在这个示例中,当点击按钮时,程序会启动Windows的记事本程序。由于使用的是start()方法,记事本与主程序处于异步运行状态,主程序不会被阻塞。

例子2:执行带有管道(|)的Linux命令

在Qt中由于QProcess不直接支持管道命令,可以通过启动shell并传递整个命令作为参数。

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

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

        process = new QProcess(this);

        connect(process, &QProcess::readyReadStandardOutput, [&]() {
            qDebug() << "Standard output:" << QString::fromLocal8Bit(process->readAllStandardOutput());
        });

        connect(process, &QProcess::readyReadStandardError, [&]() {
            qDebug() << "Standard error:" << QString::fromLocal8Bit(process->readAllStandardError());
        });

        connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
        [&](int exitCode, QProcess::ExitStatus exitStatus)
        {
            qDebug() << "Process finished with exit code" << exitCode << exitStatus;
        });

        process->start("bash", QStringList() << "-c" << "ps -ef | grep firefox");

        process->waitForFinished();
    }

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

说明:通过启动bash并传递-c参数,可以执行带有管道的复杂命令。

同步进程API

QProcess提供了一组可以在没有事件循环的情况下使用的函数,通过挂起调用线程直到发出某些信号。这些方法包括:

  • waitForStarted():阻塞直到进程开始。
  • waitForReadyRead():阻塞直到有新数据可在当前读取通道上读取为止。
  • waitForBytesWritten():阻塞直到将一个有效载荷数据写入该进程为止。
  • waitForFinished():阻塞直到过程完成。

示例

QProcess gzip;
gzip.start("gzip", QStringList() << "-c");
if (!gzip.waitForStarted())
    return false;

gzip.write("Qt rocks!");
gzip.closeWriteChannel();

if (!gzip.waitForFinished())
    return false;

QByteArray result = gzip.readAll();

注意:从主线程(调用QApplication::exec()函数的线程)调用这些函数可能会导致用户界面卡住。建议在子线程中使用这些方法,或使用信号槽机制进行异步处理。

Boost.Interprocess简介

功能:

Boost.Interprocess是Boost库中的一个模块,是专门用于实现跨进程通信

  • 共享内存:多个进程可以访问同一块内存区域
  • 命名信号量与互斥锁:用于同步进程间操作,防止资源竞争
  • 消息队列:允许进程之间以消息为单位进行通信
  • 映射文件:在不同进程间共享文件内容

用法:

(一)共享内存:
  1. 创建和写入共享内存(生产者)
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>

using namespace boost::interprocess;

void createSharedMemory() {
    // 删除已存在的共享内存
    shared_memory_object::remove("MySharedMemory");

    // 创建共享内存对象
    shared_memory_object shm(create_only, "MySharedMemory", read_write);

    // 设置共享内存大小
    shm.truncate(1024);

    // 映射共享内存
    mapped_region region(shm, read_write);

    // 写入数据
    const char *message = "Hello from producer!";
    std::memcpy(region.get_address(), message, std::strlen(message) + 1);
}
  1. 读取共享内存(消费者)
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>

using namespace boost::interprocess;

void readSharedMemory() {
    // 打开已存在的共享内存
    shared_memory_object shm(open_only, "MySharedMemory", read_only);

    // 映射共享内存
    mapped_region region(shm, read_only);

    // 读取数据
    const char *message = static_cast<const char*>(region.get_address());
    std::cout << "Message: " << message << std::endl;
}
(二)命名信号量与互斥锁示例
  1. 生产者
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/named_semaphore.hpp>

using namespace boost::interprocess;

void producer() {
    // 创建命名互斥锁
    named_mutex mutex(create_only, "MyMutex");

    // 创建命名信号量
    named_semaphore semaphore(create_only, "MySemaphore", 0);

    // 锁定互斥锁
    scoped_lock<named_mutex> lock(mutex);

    // 执行共享内存写入操作
    // ...

    // 释放互斥锁后通知消费者
    lock.unlock();
    semaphore.post();
}
  1. 消费者
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/named_semaphore.hpp>

using namespace boost::interprocess;

void consumer() {
    // 打开已存在的命名互斥锁
    named_mutex mutex(open_only, "MyMutex");

    // 打开已存在的命名信号量
    named_semaphore semaphore(open_only, "MySemaphore");

    // 等待信号量通知
    semaphore.wait();

    // 锁定互斥锁
    scoped_lock<named_mutex> lock(mutex);

    // 执行共享内存读取操作
    // ...
}
(三) 资源清理

在使用完共享内存和同步对象后,务必进行资源清理,以避免资源泄漏。

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/named_semaphore.hpp>

using namespace boost::interprocess;

void cleanup() {
    shared_memory_object::remove("MySharedMemory");
    named_mutex::remove("MyMutex");
    named_semaphore::remove("MySemaphore");
}

实现多进程

使用QProcess管理多个进程

QProcess不仅可以启动和管理单个外部进程,还支持同时管理多个进程。以下示例展示了如何启动两个外部ping进程并分别管理它们的输出:

#include <QProcess>
#include <QList>
#include <QDebug>

class ProcessManager : public QObject {
    Q_OBJECT
public:
    ProcessManager(QObject *parent = nullptr) : QObject(parent) {}

    void startProcesses() {
        // 启动第一个进程
        QProcess *process1 = new QProcess(this);
        connect(process1, &QProcess::readyReadStandardOutput, this, [=]() {
            qDebug() << "Process1 Output:" << process1->readAllStandardOutput();
        });
        process1->start("ping", QStringList() << "www.example.com");

        // 启动第二个进程
        QProcess *process2 = new QProcess(this);
        connect(process2, &QProcess::readyReadStandardOutput, this, [=]() {
            qDebug() << "Process2 Output:" << process2->readAllStandardOutput();
        });
        process2->start("ping", QStringList() << "www.google.com");

        // 存储进程以便后续管理
        processes.append(process1);
        processes.append(process2);
    }

    void stopAllProcesses() {
        foreach(QProcess *process, processes) {
            if (process->state() == QProcess::Running) {
                process->terminate();
                process->waitForFinished(3000);
            }
            process->deleteLater();
        }
        processes.clear();
    }

private:
    QList<QProcess*> processes;
};
结合Boost.Interprocess实现进程间通信

在多进程应用中,进程间通信是关键。结合QProcess和Boost.Interprocess,可以实现高效的通信机制。以下示例展示了主进程与子进程通过共享内存进行通信。

下面例子如何在Qt应用中使用QProcess启动子进程,并通过Boost.Interprocess的共享内存和互斥锁实现通信与同步。

主进程代码:

#include <QCoreApplication>
#include <QProcess>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <cstring>
#include <iostream>

using namespace boost::interprocess;

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 清理之前的资源
    shared_memory_object::remove("SharedMemory");
    named_mutex::remove("MyMutex");

    // 创建共享内存
    shared_memory_object shm(create_only, "SharedMemory", read_write);
    shm.truncate(1024);
    mapped_region region(shm, read_write);

    // 创建命名互斥锁
    named_mutex mutex(create_only, "MyMutex");

    // 启动子进程
    QProcess child;
    child.start("child_process_executable"); // 替换为实际子进程可执行文件路径

    if (!child.waitForStarted()) {
        qDebug() << "Failed to start child process.";
        return -1;
    }

    // 锁定互斥锁并写入数据
    {
        scoped_lock<named_mutex> lock(mutex);
        const char *message = "Hello from parent!";
        std::memcpy(region.get_address(), message, std::strlen(message) + 1);
    } // 自动解锁

    // 等待子进程完成
    child.waitForFinished();

    // 读取子进程的响应
    {
        scoped_lock<named_mutex> lock(mutex);
        const char *response = static_cast<const char*>(region.get_address());
        std::cout << "Response from child: " << response << std::endl;
    }

    // 清理资源
    shared_memory_object::remove("SharedMemory");
    named_mutex::remove("MyMutex");

    return a.exec();
}

说明

  1. 资源清理:在创建共享内存和互斥锁之前,先移除之前可能残留的资源,避免冲突。
  2. 创建共享内存:通过shared_memory_object创建一个名为SharedMemory的共享内存,并映射到地址空间。
  3. 创建命名互斥锁:通过named_mutex创建一个名为MyMutex的互斥锁,用于同步访问共享内存。
  4. 启动子进程:使用QProcess启动子进程child_process_executable
  5. 写入数据:在锁定互斥锁后,将消息写入共享内存。
  6. 读取响应:等待子进程完成后,读取子进程在共享内存中写入的响应。

子进程代码:

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <cstring>
#include <iostream>

using namespace boost::interprocess;

int main() {
    try {
        // 打开已存在的共享内存
        shared_memory_object shm(open_only, "SharedMemory", read_write);
        mapped_region region(shm, read_write);

        // 打开已存在的命名互斥锁
        named_mutex mutex(open_only, "MyMutex");

        // 读取主进程的数据
        {
            scoped_lock<named_mutex> lock(mutex);
            const char *message = static_cast<const char*>(region.get_address());
            std::cout << "Received from parent: " << message << std::endl;

            // 写入响应到共享内存
            const char *response = "Hello from child!";
            std::memcpy(region.get_address(), response, std::strlen(response) + 1);
        } // 自动解锁
    }
    catch(interprocess_exception &ex){
        std::cerr << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

说明

  1. 打开共享内存和互斥锁:子进程通过open_only模式打开主进程创建的共享内存和互斥锁。
  2. 读取数据:在锁定互斥锁后,读取共享内存中的数据。
  3. 写入响应:将响应消息写入共享内存。

注意:请确保子进程的可执行文件路径正确,并且子进程能够访问Boost.Interprocess库。


 


http://www.kler.cn/a/449322.html

相关文章:

  • sfnt-pingpong -测试网络性能和延迟的工具
  • 重温设计模式--外观模式
  • SQL server学习09-数据库编程(上)
  • JVM系列(十三) -常用调优工具介绍
  • LLaMA-Factory(二)界面解析
  • systemverilog中task的disable用法
  • Opencv之对图片的处理和运算
  • 【初阶数据结构与算法】八大排序算法之交换排序(冒泡排序,快速排序---hoare、挖坑法、lomuto双指针3种版本)
  • RCE 命令执行漏洞 过滤模式 基本的过滤问题 联合ctf题目进行实践
  • 【蓝桥杯——物联网设计与开发】拓展模块4 - 脉冲模块
  • CentOS7网络配置,解决不能联网、ping不通外网、主机的问题
  • 使用 Python 实现 WebSocket 服务器与客户端通信
  • 【Unity Shader】【图形渲染】Shader数学基础9 - 缩放矩阵
  • html 通用错误页面
  • 航模锂电池使用
  • GESP CCF C++六级编程等级考试认证真题 2024年12月
  • 安全删除硬件并弹出媒体(弹出显卡)问题处理
  • 大模型系列——投机解码:Prompt Lookup Decoding代码解读
  • 使用pdf2zh遇到的问题
  • 海天味业:困境突围,再寻增长
  • CV实战项目----YOLO
  • SoftMoE:From sparse to soft mixtures of experts
  • Postman集合转JMeter脚本
  • AI应用-本地模型实现AI生成PPT(简易版)
  • C++ 函数编程题
  • 远程医疗:科技助力健康触手可及