操作系统:进程间通信方式详解(下:消息队列、信号量、共享内存、套接字)

每日一问:操作系统:进程间通信方式详解(下:消息队列、信号量、共享内存、套接字)

进程间通信(Inter-Process Communication,IPC)是操作系统中实现不同进程之间数据交换和协作的关键机制。本文详细介绍了几种常用的 IPC 方式,包括消息队列、信号量、共享内存和套接字。每种通信方式都有其独特的应用场景和优势,而现有的介绍往往局限于概念的介绍,本文则结合实际应用,通过极为详细的可以运行的 C++ 和 Java 示例代码,帮助读者理解这些机制的实现原理和应用场景。 对于无名管道、有名管道、高级管道,则可见操作系统:进程间通信方式详解(上:无名管道、有名管道、高级管道)。


文章目录

  • 每日一问:操作系统:进程间通信方式详解(下:消息队列、信号量、共享内存、套接字)
    • 一、进程间通信概述
    • 二、消息队列(Message Queue)
      • 2.1 消息队列的定义与特点
      • 2.2 消息队列的C++示例代码
      • 2.3 消息队列的Java示例代码
    • 三、信号量(Semaphore)
      • 3.1 信号量的定义与特点
      • 3.2 信号量的C++示例代码
      • 3.3 信号量的Java示例代码
    • 四、共享内存(Shared Memory)
      • 4.1 共享内存的定义与特点
      • 4.2 共享内存的C++示例代码
      • 4.3 共享内存的Java示例代码
    • 五、套接字(Socket)
      • 5.1 套接字的定义与特点
      • 5.2 套接字的实现
        • C++ 示例代码(TCP 套接字通信)
        • Java 示例代码(TCP 套接字通信)
    • 六、总结(前面可以不看,这里表格总结必看)


本文深入讲解了消息队列、信号量、共享内存和套接字的定义、特点及实际应用,结合代码示例展示了这些 IPC 方式在进程间数据传输与同步中的应用。文章适合对进程间通信感兴趣的初学者和开发人员,通过示例代码掌握 IPC 机制的具体实现。


一、进程间通信概述

进程是操作系统的基本执行单位,每个进程有独立的内存空间。由于这种独立性,进程之间无法直接访问对方的数据,进程间通信(IPC)机制因此应运而生。常见的 IPC 方式包括无名管道、有名管道、消息队列、信号量、共享内存和套接字等。这些方式在数据传输效率、同步机制和复杂度上各不相同,适用于不同的应用场景。在接下来的章节,将介绍消息队列、信号量、共享内存和套接字,对于无名管道、有名管道、高级管道,则可见操作系统:进程间通信方式详解(上:无名管道、有名管道、高级管道)。

二、消息队列(Message Queue)

2.1 消息队列的定义与特点

消息队列是一种基于消息传递的通信机制,允许进程通过消息队列发送和接收消息。消息队列支持异步通信,发送方和接收方不需要同时工作。消息队列的特点是消息可以按优先级和顺序存储,便于进程之间有序交换数据。

2.2 消息队列的C++示例代码

以下是一个消息队列的 C++ 示例代码,通过 msggetmsgsndmsgrcv 系统调用创建和操作消息队列:

#include <iostream>  // 标准输入输出库
#include <sys/ipc.h>  // IPC 机制相关函数
#include <sys/msg.h>  // 消息队列相关函数
#include <cstring>  // 字符串操作库// 定义消息结构体
struct msg_buffer {long msg_type;  // 消息类型,必须为正整数char msg_text[100];  // 消息内容
};int main() {key_t key;int msgid;msg_buffer message;// 使用 ftok 生成消息队列的唯一键key = ftok("progfile", 65);  // "progfile" 文件名,65 是一个任意数值// 使用 msgget 创建消息队列,如果不存在则创建,权限设置为 0666msgid = msgget(key, 0666 | IPC_CREAT);message.msg_type = 1;  // 设置消息类型为 1// 写入消息到消息队列std::cout << "Write Message: ";std::cin.getline(message.msg_text, sizeof(message.msg_text));  // 从控制台读取消息msgsnd(msgid, &message, sizeof(message), 0);  // 发送消息到队列// 读取消息队列msgrcv(msgid, &message, sizeof(message), 1, 0);  // 接收消息类型为 1 的消息std::cout << "Received Message: " << message.msg_text << std::endl;  // 打印接收到的消息// 删除消息队列msgctl(msgid, IPC_RMID, NULL);  // 删除消息队列,清理资源return 0;
}

解释

  1. ftok():生成唯一的键值,用于识别消息队列。
  2. msgget():创建一个新的消息队列或获取一个已存在的消息队列。
  3. msgsnd():向消息队列发送消息。
  4. msgrcv():从消息队列接收消息。
  5. msgctl():控制消息队列,如删除队列。

2.3 消息队列的Java示例代码

Java 没有直接的消息队列实现,可以通过 BlockingQueue 类进行模拟:

import java.util.concurrent.BlockingQueue;  // 导入 BlockingQueue 接口,用于实现阻塞队列
import java.util.concurrent.LinkedBlockingQueue;  // 导入 LinkedBlockingQueue 类,实现线程安全的阻塞队列public class MessageQueueExample {// 创建一个阻塞队列用于模拟消息队列private static BlockingQueue<String> queue = new LinkedBlockingQueue<>();public static void main(String[] args) throws InterruptedException {// 创建发送线程Thread sender = new Thread(() -> {try {queue.put("Hello from sender!");  // 向队列中放入消息} catch (InterruptedException e) {e.printStackTrace();  // 捕获并打印异常}});// 创建接收线程Thread receiver = new Thread(() -> {try {String message = queue.take();  // 从队列中取出消息System.out.println("Received: " + message);  // 输出接收到的消息} catch (InterruptedException e) {e.printStackTrace();  // 捕获并打印异常}});sender.start();  // 启动发送线程receiver.start();  // 启动接收线程sender.join();  // 等待发送线程结束receiver.join();  // 等待接收线程结束}
}

解释

  1. BlockingQueue:Java 中用于线程间通信的阻塞队列,模拟消息队列的异步特性。
  2. put()take():分别用于将消息放入队列和从队列中取出消息,实现发送和接收操作。

三、信号量(Semaphore)

3.1 信号量的定义与特点

信号量是一种用于进程间同步的计数器机制,可以控制多个进程对共享资源的访问。信号量经常与共享内存结合使用,解决并发访问问题,确保资源不会被多个进程同时访问而导致数据冲突。

3.2 信号量的C++示例代码

以下是一个简单的 C++ 信号量示例,演示如何使用信号量控制线程对临界区的访问:

#include <iostream>  // 标准输入输出库
#include <pthread.h>  // POSIX 线程库
#include <semaphore.h>  // 信号量库sem_t semaphore;  // 定义信号量// 线程执行的任务函数
void* task(void* arg) {sem_wait(&semaphore);  // 尝试获取信号量,信号量值减 1std::cout << "Entered critical section" << std::endl;  // 打印消息表示进入临界区sem_post(&semaphore);  // 释放信号量,信号量值加 1return NULL;
}int main() {pthread_t t1, t2;  // 定义两个线程sem_init(&semaphore, 0, 1);  // 初始化信号量,0 表示信号量用于线程间同步,初始值为 1// 创建两个线程执行任务pthread_create(&t1, NULL, task, NULL);pthread_create(&t2, NULL, task, NULL);// 等待两个线程执行完毕pthread_join(t1, NULL);pthread_join(t2, NULL);sem_destroy(&semaphore);  // 销毁信号量,释放资源return 0;
}

解释

  1. sem_init():初始化信号量,指定信号量初始值。
  2. sem_wait():等待信号量,可进入临界区时信号量值减 1。
  3. sem_post():释放信号量,信号量值加 1。
  4. sem_destroy():销毁信号量,清理资源。

3.3 信号量的Java示例代码

Java 通过 java.util.concurrent.Semaphore 类实现信号量控制:

import java.util.concurrent.Semaphore;  // 导入 Semaphore 类public class SemaphoreExample {private static Semaphore semaphore = new Semaphore(1);  // 创建信号量,初始值为 1public static void main(String[] args) {// 定义线程任务Runnable task = () -> {try {semaphore.acquire();  // 获取信号量,阻塞直到信号量可用System.out.println("Entered critical section");  // 打印进入临界区的消息semaphore.release();  // 释放信号量} catch (InterruptedException e) {e.printStackTrace();  // 捕获并打印异常}};// 创建并启动两个线程Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();}
}

解释

  1. acquire():尝试获取信号量,信号量不足时阻塞。
  2. release():释放信号量,允许其他线程进入临界区。

四、共享内存(Shared Memory)

4.1 共享内存的定义与特点

共享内存是最直接的进程间通信方式,通过多个进程共享一块内存区域来进行数据交换。共享内存提供了最快的通信速度,但需要借助同步机制来防止数据冲突。

4.2 共享内存的C++示例代码

以下代码展示了共享内存的使用方法,通过 shmgetshmat 系统调用来创建和连接共享内存:

#include <iostream>  // 标准输入输出库
#include <sys/ipc.h>  // IPC 机制相关函数
#include <sys/shm.h>  // 共享内存相关函数
#include <cstring>  // 字符串操作库int main() {// 创建共享内存键值,"shmfile" 是用于生成键值的路径,65 是任意选定的整数key_t key = ftok("shmfile", 65);// 创建共享内存,大小为 1024 字节,权限为 0666,若不存在则创建int shmid = shmget(key, 1024, 0666 | IPC_CREAT);// 连接到共享内存,返回一个指向共享内存的指针char *str = (char*) shmat(shmid, (void*)0, 0);strcpy(str, "Hello Shared Memory!");  // 向共享内存写入数据std::cout << "Data written in memory: " << str << std::endl;  // 输出写入的数据shmdt(str);  // 断开共享内存连接shmctl(shmid, IPC_RMID, NULL);  // 删除共享内存,清理资源return 0;
}

解释

  1. shmget():创建共享内存段,指定大小和权限。
  2. shmat():将共享内存附加到进程地址空间,返回指向共享内存的指针。
  3. shmdt():将共享内存从当前进程地址空间分离。
  4. shmctl():控制共享内存,包括删除共享内存段。

4.3 共享内存的Java示例代码

Java 通过 MappedByteBuffer 类实现类似共享内存的功能:

import java.io.RandomAccessFile;  // 导入 RandomAccessFile 类,用于文件读写
import java.nio.MappedByteBuffer;  // 导入 MappedByteBuffer 类,用于内存映射
import java.nio.channels.FileChannel;  // 导入 FileChannel 类,用于文件通道操作public class SharedMemoryExample {public static void main(String[] args) throws Exception {// 创建或打开文件 "shared_memory.bin",读写模式RandomAccessFile file = new RandomAccessFile("shared_memory.bin", "rw");// 将文件映射到内存,映射模式为读写,大小为 1024 字节MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024);buffer.put("Hello Shared Memory!".getBytes());  // 将数据写入内存映射buffer.flip();  // 重置缓冲区位置以便读取byte[] data = new byte[buffer.remaining()];  // 创建字节数组保存读取的数据buffer.get(data);  // 从缓冲区读取数据System.out.println("Data read from memory: " + new String(data));  // 输出读取的数据file.close();  // 关闭文件}
}

解释

  1. MappedByteBuffer:将文件的某一部分映射到内存,允许直接对文件数据进行读写。
  2. map():将文件通道中的数据映射到内存区域。
  3. flip():重置缓冲区位置,以便后续的读取操作。

五、套接字(Socket)

5.1 套接字的定义与特点

套接字(Socket)是一种支持本地和网络通信的进程间通信方式,可以在本地进程间或跨网络的不同计算机之间进行双向通信。套接字支持 TCP(可靠传输)和 UDP(不可靠但高效)两种协议。

5.2 套接字的实现

套接字是通信端点,通过绑定 IP 地址和端口号来进行数据交换。

C++ 示例代码(TCP 套接字通信)
#include <iostream>  // 标准输入输出库
#include <sys/socket.h>  // 套接字库
#include <arpa/inet.h>  // 地址转换库
#include <unistd.h>  // POSIX 操作库int main() {int server_fd, new_socket;  // 定义服务器套接字和新连接套接字struct sockaddr_in address;  // 定义地址结构体int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};  // 定义缓冲区用于接收数据const char *hello = "Hello from server";  // 定义发送给客户端的消息// 创建套接字,使用 IPv4 地址族,TCP 流式套接字server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == 0) {  // 检查套接字创建是否成功perror("socket failed");return 1;}// 设置套接字选项,允许地址和端口重用setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));// 绑定套接字到指定的 IP 地址和端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;  // 使用本地所有可用的 IP 地址address.sin_port = htons(8080);  // 端口号 8080if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");return 1;}// 监听端口,最大等待连接数为 3if (listen(server_fd, 3) < 0) {perror("listen failed");return 1;}// 接受客户端连接new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen);if (new_socket < 0) {perror("accept failed");return 1;}// 读取客户端消息read(new_socket, buffer, 1024);std::cout << "Message from client: " << buffer << std::endl;// 发送回复给客户端send(new_socket, hello, strlen(hello), 0);std::cout << "Hello message sent" << std::endl;// 关闭套接字close(new_socket);close(server_fd);return 0;
}
Java 示例代码(TCP 套接字通信)
import java.io.*;  // 导入输入输出类
import java.net.*;  // 导入网络类public class SocketServer {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8080)) {  // 创建服务器套接字绑定到端口 8080System.out.println("Server started, waiting for connection...");// 等待客户端连接Socket socket = serverSocket.accept();System.out.println("Client connected.");// 创建输入输出流BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter output = new PrintWriter(socket.getOutputStream(), true);// 读取客户端消息String clientMessage = input.readLine();System.out.println("Received from client: " + clientMessage);// 回复客户端output.println("Hello from server!");// 关闭连接socket.close();} catch (IOException e) {e.printStackTrace();}}
}

解释

  1. ServerSocket:服务器端套接字,监听指定端口。
  2. accept():等待客户端连接,建立连接后返回客户端的套接字。
  3. BufferedReaderPrintWriter:用于处理输入输出流,读取客户端发送的数据并进行响应。

六、总结(前面可以不看,这里表格总结必看)

进程间通信(IPC)是操作系统中实现进程间数据交换和同步的关键技术。不同的 IPC 方式在性能、适用场景、易用性上各有特点:

通信方式定义特点应用场景
无名管道单向数据传输,父子进程间简单、只支持亲缘进程父子进程间数据传输
有名管道有名且持久,支持无亲缘进程双向、需文件系统支持任意进程间的数据传输
高级管道通过子进程执行命令并传输数据创建灵活,可执行命令结果执行系统命令,获取输出
消息队列基于消息的通信异步、按优先级排序异步任务处理
信号量计数器机制,控制资源访问同步、解决并发冲突多进程资源访问控制
共享内存共享内存区域快速传输数据高速、需同步机制需高效通信的场景
套接字本地和网络通信支持双向、网络和本地通信网络应用、跨主机进程间通信

下面给出更复杂版本的对比表格:

通信方式数据传输方向是否支持无亲缘关系进程同步与异步速度数据持久性编程复杂性典型应用场景
无名管道单向同步中等不持久父子进程间简单数据传输
有名管道双向同步中等不持久无亲缘关系进程间数据传输
高级管道单向同步中等不持久父子进程间调用系统命令或可执行程序
消息队列单向异步中等不持久多个进程间复杂数据交换
信号量N/A同步N/AN/A进程/线程同步,解决资源争用问题
共享内存双向同步(需同步机制)不持久大量数据的快速读写,需同步控制
套接字双向是(支持网络通信)同步/异步视网络环境而定不持久本地或网络进程间的复杂数据通信

通过正确选择 IPC 机制,开发者可以有效实现进程间的数据交换和同步,提升系统的响应速度和稳定性。根据实际需求,选择合适的进程间通信方式,可以最大限度地提高应用程序的性能和可靠性。

✨ 我是专业牛,一个渴望成为大牛🏆的985硕士🎓,热衷于分享知识📚,帮助他人解决问题💡,为大家提供科研、竞赛等方面的建议和指导🎯。无论是科研项目🛠️、竞赛🏅,还是图像🖼️、通信📡、计算机💻领域的论文辅导📑,我都以诚信为本🛡️,质量为先!🤝

如果你觉得这篇文章对你有所帮助,别忘了点赞👍、收藏📌和关注🔔!你的支持是我继续分享知识的动力🚀!✨ 如果你有任何问题或需要帮助,随时留言📬或私信📲,我都会乐意解答!😊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/54470.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【有啥问啥】深度剖析:大模型AI时代下的推理路径创新应用方法论

深度剖析&#xff1a;大模型AI时代下的推理路径创新应用方法论 随着大规模预训练模型&#xff08;Large Pretrained Models, LPMs&#xff09;和生成式人工智能的迅速发展&#xff0c;AI 在多领域的推理能力大幅提升&#xff0c;尤其是在自然语言处理、计算机视觉和自动决策领…

Kafka 下载安装及使用总结

1. 下载安装 官网下载地址&#xff1a;Apache Kafka 下载对应的文件 上传到服务器上&#xff0c;解压 tar -xzf kafka_2.13-3.7.0.tgz目录结果如下 ├── bin │ └── windows ├── config │ └── kraft ├── libs ├── licenses └── site-docs官方文档…

Flink Task 日志文件隔离

Flink Task 日志文件隔离 任务在启动时会先通过 MdcUtils 启动一个 slf4j 的 MDC 环境&#xff0c;然后将 jobId 添加到 slf4j 的 MDC 容器中&#xff0c;随后任务输出的日志都将附带 joid。 MDC 介绍如下&#xff1a; MDC ( Mapped Diagnostic Contexts )&#xff0c;它是一个…

深度学习:(六)激活函数的选择与介绍

激活函数 之前使用的 a σ ( z ) a\sigma(z) aσ(z) &#xff0c;其中 σ ( ) \sigma(~) σ( ) 便是激活函数。 在神经网络中&#xff0c;不同层的激活函数可以不同。 在学习中&#xff0c;一般以 g ( z ) g(z) g(z) 来表示激活函数。 为什么需要(线性)激活函数&#xff…

K8s容器运行时,移除Dockershim后存在哪些疑惑?

K8s容器运行时&#xff0c;移除Dockershim后存在哪些疑惑&#xff1f; 大家好&#xff0c;我是秋意零。 K8s版本截止目前&#xff08;24/09&#xff09;已经发布到了1.31.x版本。早在K8s版本从1.24.x起&#xff08;22/05&#xff09;&#xff0c;默认的容器运行时就不再是Doc…

算法之搜索--最长公共子序列LCS

最长公共子序列&#xff08;longest common sequence&#xff09;:可以不连续 最长公共子串&#xff08;longest common substring&#xff09;&#xff1a;连续 demo for (int i 1;i<lena;i){for (int j 1;j<lenb;j){if(a[i-1]b[j-1]){dp[i][j]dp[i-1][j-1]1;}el…

Qt (17)【Qt 文件操作 读写保存】

阅读导航 引言一、Qt文件概述二、输入输出设备类三、文件读写类四、文件和目录信息类五、自定义“记事本” 引言 在上一篇文章中&#xff0c;我们学习了Qt的事件处理机制&#xff0c;知道了如何响应用户的操作。但应用程序常常还需要处理文件&#xff0c;比如读写数据。所以&a…

python爬虫初体验(一)

文章目录 1. 什么是爬虫&#xff1f;2. 为什么选择 Python&#xff1f;3. 爬虫小案例3.1 安装python3.2 安装依赖3.3 requests请求设置3.4 完整代码 4. 总结 1. 什么是爬虫&#xff1f; 爬虫&#xff08;Web Scraping&#xff09;是一种从网站自动提取数据的技术。简单来说&am…

指针修仙之实现qsort

文章目录 回调函数什么是回调函数回调函数的作用 库函数qsort使用qsort函数排序整形使用qsort函数排序结构体 qsort函数模拟实现说明源码and说明 回调函数 什么是回调函数 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数…

Sigmoid引发的梯度消失爆炸及ReLU引起的神经元参数失效问题思考

Sigmoid和ReLU激活函数思考&#xff09; 引文Sigmoid函数梯度消失问题梯度爆炸问题解决方案 ReLU函数简化模型示例场景设定前向传播对反向传播的影响总结 内容精简版 引文 梯度消失和梯度爆炸是神经网络训练中常见的两个问题&#xff0c;特别是在使用Sigmoid激活函数时。这些问…

后端-navicat查找语句(单表与多表)

表格字段设置如图 语句&#xff1a; 1.输出 1.输出name和age列 SELECT name,age from student 1.2.全部输出 select * from student 2.where子语句 1.运算符&#xff1a; 等于 >大于 >大于等于 <小于 <小于等于 ! <>不等于 select * from stude…

torch模型量化方法总结

0.概述 模型训练完成后的参数为float或double类型,而装机(比如车载)后推理预测时,通常都会预先定点(量化)为int类型参数,相应的推理的精度会有少量下降,但不构成明显性能下降,带来的结果是板端部署的可能性,推理的latency明显降低,本文对torch常用的量化方法进行总…

JavaEE: 创造无限连接——网络编程中的套接字

文章目录 Socket套接字TCP和UDP的区别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP api的使用UDPDatagramSocketDatagramPacketInetSocketAddress练习 TCPServerSocketSocket练习 Socket套接字 Socket是计算机网络中的一种通信机制&#xff0…

【VLM小白指北 (1) 】An Introduction to Vision-Language Modeling

开一个新坑Vision-Language Modeling (VLM) &#xff0c;原文76页&#xff0c;慢慢更&#xff0c;for beginners&#xff0c;但也不能之前啥都不会啊。 原文链接&#xff1a;An Introduction to Vision-Language Modeling Introduction 存在的问题&#xff1a;将语言与视觉相…

ChatGPT 在国内使用的方法

AI如今很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;ChatGPT 真是无所不能~ 作为一款出色的大语言模型&#xff0c;ChatGPT 实现了人类般的对话交流&#xff0c;最主要是能根据上下文进行互动。 接下来&#xff0c;我将介绍 ChatGPT 在国…

xhs 小红书 x-s web 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 有相关问题请第一时间头像私信联系我…

《深度学习》PyTorch框架 优化器、激活函数讲解

目录 一、深度学习核心框架的选择 1、TensorFlow 1&#xff09;概念 2&#xff09;优缺点 2、PyTorch 1&#xff09;概念 2&#xff09;优缺点 3、Keras 1&#xff09;概念 2&#xff09;优缺点 4、Caffe 1&#xff09;概念 2&#xff09;优缺点 二、pytorch安装 1、安装 2、…

Linux操作系统:GCC(GNU Compiler Collection)编译器

在 Linux 系统中&#xff0c;gcc&#xff08;GNU Compiler Collection&#xff09;是一个非常强大的编译器&#xff0c;主要用于编译 C 语言程序。 除了基本的编译和链接命令外&#xff0c;gcc还提供了许多选项和功能。 以下是一些常用的 gcc命令及其功能&#xff1a; 1. 基本…

Python | Leetcode Python题解之第420题强密码检验器

题目&#xff1a; 题解&#xff1a; class Solution:def strongPasswordChecker(self, password: str) -> int:n len(password)has_lower has_upper has_digit Falsefor ch in password:if ch.islower():has_lower Trueelif ch.isupper():has_upper Trueelif ch.isdi…

基于SpringBoot+Vue的智慧物业管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目源码、Python精…