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的使用。

相关推荐
猷咪9 分钟前
C++基础
开发语言·c++
IT·小灰灰10 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧12 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q13 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳013 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾13 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB16 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683617 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计31 分钟前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
蒹葭玉树42 分钟前
【C++上岸】C++常见面试题目--操作系统篇(第二十八期)
linux·c++·面试