Qt多进程(一)进程间通信概括

前言

在之前的文章中,已经较为详细地探讨了Qt中线程的使用和线程间的通信,但对于多进程,我一直了解得比较浅薄。在早起学习Linux的时候,就有一堆让人容易混淆的进程间通信方式,什么有名管道无名管道、信号和信号量、共享内存、socket套接字什么的,当时也只是简单跟着教程跑了一遍,其实也一知半解的。最近找工作时,突然被问了一下对多进程有多少了解,我当时只回答了:"我在调用打开另一个exe的时候,我用了tcp的方式和它连接和通信",面试官说是本地socket吗,我说应该是吧,他又问除此之外还知道哪些?我尴尬摇头......显然,这样的回答实在是差强人意。

于是,我也打算"稍微"深入了解和实践一下多进程相关的东西,起码下一次被人问到,就能给出一个合格的回答。当然,多进程的通信方式有很多,特别是在不同架构系统下的方式都不大一样,我也不可能一下子全部都掌握。我的当前开发环境是Windows下的Qt6.10,之后的尝试都是基于这个环境下进行。

一、IPC通信方式

IPC,即Inter-Process Communication,也被称为进程间通信,是指在操作系统中,不同进程之间进行数据交换和信息共享的一种技术或机制。此后多进程这个概念,我可能会用IPC来指代。

正式开始之前,我尝试让AI帮忙总结推荐一下,得到了以下回答:

这里说的其实也不太全面,但前四种都还蛮常见的,适合深入学习一下的。

我打算后续每一种方式都用单独的一篇文章来记录。

二、测试工程的搭建

多进程,顾名思义需要双开两个exe,比如一个当服务端,一个当客户端。但考虑到开发测试的复杂程度,我决定还是将所有功能整合在同一个测试工程里面,命名为IPCTest。也就是说,实际测试的时候,需要打开两次IPCTest.exe。

因此,这个工程不仅要整合所有通信方式,还要支持服务端客户端的选择,因此我在主界面中用下拉选择框的方式供用户选择:

dart 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QDialog>

class MainWindow : public QWidget
{
    Q_OBJECT

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

private:
    void setupUI();

    QVBoxLayout *mainLayout;
    QLabel *titleLabel;
    QPushButton *startButton;

    QComboBox *ipcCombo;
    QComboBox *roleCombo;
};

#endif // MAINWINDOW_H
dart 复制代码
#include "mainwindow.h"
#include "ipcselector.h"
#include <QApplication>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle("IPC Test - Main Window");
    resize(400, 300);

    setupUI();
}

void MainWindow::setupUI()
{
    mainLayout = new QVBoxLayout(this);

    titleLabel = new QLabel("IPC Test Program\nPlease select IPC method and role");
    titleLabel->setAlignment(Qt::AlignCenter);
    titleLabel->setStyleSheet("font-size: 16px; font-weight: bold; margin: 20px;");

    startButton = new QPushButton("Start Test");

    ipcCombo = new QComboBox();
    ipcCombo->addItem("TCP Socket");
    ipcCombo->addItem("Local Socket");
    ipcCombo->addItem("Shared Memory");
    ipcCombo->addItem("File Queue");
    ipcCombo->addItem("Memory Map");
    ipcCombo->addItem("UDP Socket");
    ipcCombo->addItem("Stdio");

    roleCombo = new QComboBox();
    roleCombo->addItem("Server");
    roleCombo->addItem("Client");

    connect(startButton, &QPushButton::clicked, this, [this]() {
        QString selectedIPC = ipcCombo->currentText();
        QString selectedRole = roleCombo->currentText();
        if (selectedIPC.isEmpty() || selectedRole.isEmpty()) {
            QMessageBox::warning(this, "Warning", "Please select both IPC method and role!");
            return;
        }

        IPCSelector::createAndShow(selectedIPC, selectedRole);
        this->close();
    });

    mainLayout->addWidget(titleLabel);
    mainLayout->addWidget(ipcCombo);
    mainLayout->addWidget(roleCombo);
    mainLayout->addWidget(startButton);
    mainLayout->addStretch();
}

运行时大概是这样子的:

点击开始后,会根据当前的选择,动态创建不同IPC通信方式对应的界面类:

dart 复制代码
#ifndef IPCSELECTOR_H
#define IPCSELECTOR_H

#include <QString>
#include <QWidget>

class IPCSelector
{
public:
    static QWidget* createAndShow(const QString &ipcType, const QString &role);
};

#endif // IPCSELECTOR_H
dart 复制代码
#include <QMessageBox>
#include "ipcselector.h"
#include "tcpwindow.h"
#include "localsocketwindow.h"
#include "sharedmemorywindow.h"
#include "filequeuewindow.h"
#include "memorymapwindow.h"
#include "udpwindow.h"
#include "stdiowindow.h"

QWidget* IPCSelector::createAndShow(const QString &ipcType, const QString &role)
{
    QWidget *window = nullptr;

    if (ipcType == "TCP Socket") {
        window = new TCPWindow(role);
    } else if (ipcType == "Local Socket") {
        window = new LocalSocketWindow(role);
    } else if (ipcType == "Shared Memory") {
        window = new SharedMemoryWindow(role);
    } else if (ipcType == "File Queue") {
        window = new FileQueueWindow(role);
    } else if (ipcType == "Memory Map") {
        window = new MemoryMapWindow(role);
    } else if (ipcType == "UDP Socket") {
        window = new UDPWindow(role);
    } else if (ipcType == "Stdio") {
        window = new StdioWindow(role);
    } else {
        QMessageBox::warning(nullptr, "Error", "Unknown IPC type: " + ipcType);
        return nullptr;
    }

    if (window) {
        window->show();
    }

    return window;
}

这种方式类似于工厂模式,能够很好地兼容后续的扩展,也具有良好的代码维护性。

三、总结

初步搭建好工程后,就可以进入后续的每一种方式的单独测试了。

希望能好好记录,完全掌握IPC的使用。

相关推荐
superman超哥2 小时前
仓颉语言中基本数据类型的深度剖析与工程实践
c语言·开发语言·python·算法·仓颉
不爱吃糖的程序媛2 小时前
Ascend C开发工具包(asc-devkit)技术解读
c语言·开发语言
bu_shuo2 小时前
MATLAB奔溃记录
开发语言·matlab
J ..3 小时前
C++ 多线程编程基础与 std::thread 使用
c++
你的冰西瓜3 小时前
C++标准模板库(STL)全面解析
开发语言·c++·stl
闻缺陷则喜何志丹3 小时前
【计算几何】仿射变换与齐次矩阵
c++·数学·算法·矩阵·计算几何
chen_ever3 小时前
Protobuf详解(从安装到实战)
c++·rpc·信息与通信
做cv的小昊3 小时前
【TJU】信息检索与分析课程笔记和练习(1)认识文献
经验分享·笔记·学习·搜索引擎·全文检索
徐先生 @_@|||3 小时前
(Wheel 格式) Python 的标准分发格式的生成规则规范
开发语言·python