【Linux】17. 进程间通信 --- 管道
1. 什么是进程间通信(进程间通信的目的)
数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
在之前的学习过程中我们了解到进程是具有独立性的,也就是说要实现通信成本一定不低
那我们为啥要实现通信呢?
是因为在使用操作系统的过程中是存在多进程协同的应用场景滴!完成某种业务需求 (例如:cat file | grep ’ hello ')
- 那么该如何理解通信的本质问题呢?
- 要实现通信,首先数据需要存放位置,如果将数据存放在某个进程当中,因为进程的独立性,直接由进程创建的资源其他进程无法看见
所以要由操作系统直接或者间接的给通信双方的进程提供"内存空间" - 要让不同的进程看到同一份公共的资源
通过资源是OS中的不同模块提供所得到不同的通信种类:如果是文件模块 – 管道通信 如果是SystemV通信模块 – System V通信…
2. 进程间通信发展
管道
- 匿名管道pipe
- 命名管道
System V IPC
- System V 消息队列
- System V 共享内存
- System V 信号量
POSIX IPC
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
其实在进程间通信的研究过程当中产生了一大堆的标准,但是主流的还是以上三种
POSIX — 让通信过程可以跨越主机
System V — 主要是聚焦在本地通信当中
管道 — 基于文件系统形成的
3. 管道
3.1 什么是管道
管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道
文件存储在磁盘当中,当数据写入到内核缓冲区时,会刷新到磁盘上
进程间通信是否会采取数据写入到磁盘上再从磁盘上读取到进程的上下文环境呢? – 不会
因为这样太慢了,我们要实现的是两进程的通信(从内存到内存) ,而将数据写入磁盘(内存 -> 磁盘 -> 内存)效率非常低
对于管道文件而言,需不需要在磁盘上占据内存空间(在磁盘上打开文件) – 不需要
操作系统非常强大,可以直接在内存当中创建文件(管道文件 – 内存级文件)
内存级文件不需要进行磁盘刷新,大大的提高了进程间通信的效率
3.2 匿名管道
上述的实现过程中是如何让两个进程看到同一个管道文件? 通过fork创建子进程完成
子进程会继承父进程的文件描述符表 父子进程会指向同一文件,但是该文件没有名字(内存级文件) 我们将其称之为匿名管道
为啥父子进程要以读写的方式打开同一文件呢?
因为子进程会继承父进程的文件描述符表和文件的打开方式,若是父进程只以读/写的方式打开文件,那么子进程就会继承对应的读写方式,无法构成管道的需求。
这里的pipefd [ 2 ] 是输出型参数。
在之前的学习过程中我们了解到只要打开一个文件,操作系统会对应打开0,1,2(标准输入,标准输出,标准错误)
那么文件描述符就是要从3开始
创建管道文件,操作系统以读写的方式打开文件,将进程的文件描述符表填写到数组当中,再将数组输出返回调用该函数就可以创建管道文件
写管道文件的描述符必须通过fd数组的下标访问,不能直接使用具体的fd:3 / 4
(因为我们并不清楚现在文件描述符当中是否还存在其他描述符)
fd【0】读取 – 0 像嘴巴
fd【1】写入 – 1 像钢笔
基本框架如下:
#include <iostream>
#include <unistd.h>
// 当我们在进行C语言、C++混编的时候
// 推荐将C语言的头文件引用为<c..>的格式
#include <cassert>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;int main()
{// 第一步:创建管道文件,打开读写端int fds[2];int n = pipe(fds);// 管道创建成功,n返回0assert(n == 0);// 第二步:fork(创建子进程)pid_t id = fork();// 创建子进程成功assert(id >= 0);if (id == 0){// 子进程模块// 关闭读接口close(fds[0]);// 进行父子进程通信close(fds[1]);exit(0);}// 父进程模块// 进行读取// 关闭写接口close(fds[1]);// 父进程进行等待n = waitpid(id, nullptr, 0);assert(n == id);close(fds[0]);
}
运行结果如下:
此时父进程完成读取,进入等待状态(R ——> S),在等待管道文件就会将父进程的PCB放入文件的等待队列当中
3.3 管道读写规则
- 当没有数据可读时
- O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
- O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
- 当管道满的时候
- O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
- O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
- 如果所有管道写端对应的文件描述符被关闭,则read返回0
- 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程
退出- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
3.4 管道特点
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
- 管道提供流式服务
- 一般而言,进程退出,管道释放,所以管道的生命周期随进程
- 一般而言,内核会对管道操作进行同步与互斥
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
sleep10000 | sleep 20000 bash命令行解释器,会将其划分成为两个进程, 竖划线创建的就是匿名管道
3.5 匿名管道进程池(重点)
1) 理清思路
我们想要实现的功能是首先父进程跟多个子进程之间建立管道,创建任务集,随机挑选子进程完成随机任务。
子进程根据父进程传入管道中的commandCode完成对应的任务。
2) 代码实现
① 加载任务集
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>/子进程要完成的某种任务 --- 模拟实现// 函数指针 类型
typedef void (*func_t)();// 这里就模拟实现了三种任务
void downLoadTask()
{// 执行任务的同时获取一下子进程的pid -- 便于观察std::cout << getpid() << ": 下载任务\n"<< std::endl;
}void ioTask()
{std::cout << getpid() << ": IO任务\n"<< std::endl;
}void flushTask()
{std::cout << getpid() << ": 刷新任务\n"<< std::endl;
}// 往out中插入任务集
void loadTaskFunc(std::vector<func_t> *out)
{assert(out);out->push_back(downLoadTask);out->push_back(ioTask);out->push_back(flushTask);
}int main()
{// 1. 加载任务集std::vector<func_t> funcMap;loadTaskFunc(&funcMap);return 0;
}
② 创建子进程
// 2. 创建子进程std::vector<subEp> subs;createSubProcess(&subs, funcMap);
#define PROCESS_NUM 4/子进程要完成的某种任务 --- 模拟实现// 函数指针 类型
typedef void (*func_t)();// 这里就模拟实现了三种任务
void downLoadTask()
{// 执行任务的同时获取一下子进程的pid -- 便于观察std::cout << getpid() << ": 下载任务\n"<< std::endl;
}void ioTask()
{std::cout << getpid() << ": IO任务\n"<< std::endl;
}void flushTask()
{std::cout << getpid() << ": 刷新任务\n"<< std::endl;
}// 往out中插入任务集
void loadTaskFunc(std::vector<func_t> *out)
{assert(out);out->push_back(downLoadTask);out->push_back(ioTask);out->push_back(flushTask);
}///下面的代码模拟实现多进程程序
class subEp
{
public:subEp(pid_t subId, int writeFd): subId_(subId), writeFd_(writeFd){char nameBuffer[1024];snprintf(nameBuffer, sizeof nameBuffer, "process-%d[pid(%d)-fd(%d)]", num++, subId_, writeFd_);name_ = nameBuffer;}public:static int num;std::string name_;pid_t subId_;int writeFd_;
};// 因为num为static类型 不属于任何一个对象(实例) 所以必须在这里进行初始化
int subEp::num = 0;int recvTask(int readFd)
{int code = 0;// 判断是否读取到管道中的数据ssize_t s = read(readFd, &code, sizeof code);// 读取到 4个字节if (s == 4)return code;// 父进程退出else if (s <= 0)return -1;elsereturn 0;
}void createSubProcess(std::vector<subEp> *subs, std::vector<func_t> &funcMap)
{for (int i = 0; i < PROCESS_NUM; i++){int fds[2];int n = pipe(fds);assert(n == 0);(void)n;pid_t id = fork();if (id == 0){close(fds[1]);while (true){// 获取管道中的任务编号,如果没有发送,子进程应该进行阻塞等待int commandCode = recvTask(fds[0]);if (commandCode >= 0 && commandCode < funcMap.size()){// 执行任务funcMap[commandCode];}else if (commandCode == -1){// 检测到父进程退出 跳出循环break;}}// 子进程退出exit(0);}// 关闭读文件描述符 fds[0]close(fds[0]);subEp sub(id, fds[1]);// 将创建出的子进程插入subEp对象当中subs->push_back(sub);}
}
这里构造的subEp(先描述再组织)用来管理子进程
③ 父进程控制子进程
// 3. 父进程控制子进程,负载均衡的向子进程发生任务码int taskCnt = 3;loadBlanceControl(subs, funcMap, taskCnt);
void sendTask(const subEp &process, int taskNum)
{std::cout << "send task num: " << taskNum << " send to -> " << process.name_ << std::endl;int n = write(process.writeFd_, &taskNum, sizeof(taskNum));assert(n == sizeof(int));(void)n;
}void loadBlanceControl(const std::vector<subEp> &subs, const std::vector<func_t> &funcMap, int count)
{int processnum = subs.size();int tasknum = funcMap.size();while (count){// 这里就是负载均衡的体现// 1. 选择一个子进程 --> std::vector<subEp> -> index - 随机数int subIdx = rand() % processnum;// 2. 选择一个任务 --> std::vector<func_t> -> indexint taskIdx = rand() % tasknum;// 3. 将任务发送给选择的进程sendTask(subs[subIdx], taskIdx);// 每次发送完休息1秒sleep(1);count--;}// 当write写入退出 说明读到0了 不再需要给子进程发送任务for (int i = 0; i < processnum; i++){close(subs[i].writeFd_); // waitpid();}
}
④ 回收子进程信息
// 4. 回收子进程信息
waitProcess(subs);
void waitProcess(std::vector<subEp> processes)
{int processnum = processes.size();for(int i =0;i<processnum;i++){waitpid(processes[i].subId_,nullptr,0);std::cout<<"wait sub process success ...: " << processes[i].subId_ << std::endl;}
}
assert断言
意料之中用assert 意料之外用if判断
(void)n的作用 assert的作用只是在debug下,在release版本下该行代码会删除
那么之前定义的n变量就没有进行使用可能就会出现warning报错:定义了变量但未使用。
3.6 命名管道
- 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
- 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
- 命名管道是一种特殊类型的文件
创建一个命名管道
- 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
- 命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *filename,mode_t mode);
匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
命名管道的打开规则
- 如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
- 如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
命名管道通信
逻辑分析
即便两个文件在进行通信,管道文件的大小仍是0
代码分析
// 头文件 + 创建/删除管道
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 定义宏 将命名管道放在当前目录下(便于观察)
#define NAMED_PIPE "./mypipe"
// 创建管道
bool createFifo(const std::string &path)
{// 将文件掩码设置为0umask(0);int n = mkfifo(path.c_str(), 0600);if (n == 0)return true;else{std::cout << "errno: " << errno << " err stirng: " << strerror(errno) << std::endl;return false;}
}// 删除管道
void removeFifo(const std::string &path)
{int n = unlink(path.c_str());assert(n == 0);(void)n;
}
mkfifo 创建管道文件,unlink 关闭文件
client端 – 客户端 往管道中写入数据
#include "comm.hpp"int main()
{std::cout << "client begin" << std::endl;// 将管道文件以只写入的方式打开int wfd = open(NAMED_PIPE, O_WRONLY);std::cout << "client end" << std::endl;// 管道文件打开失败if (wfd < 0)exit(1);// writechar buffer[1024];while (true){std::cout << "Please say# ";fgets(buffer, sizeof(buffer), stdin);// 当管道中有数据 将最后一位置为0// 这里的if判断buffer>0虽然没必要 因为输入数据至少要按下回车键 就一定有数据// 但是逻辑正确if (strlen(buffer) > 0)buffer[strlen(buffer) - 1] = 0;ssize_t n = write(wfd, buffer, strlen(buffer));assert(n == strlen(buffer));(void)n;}close(wfd);return 0;
}
server端 — 服务端 从管道当中读出数据
#include "comm.hpp"// sever作为主控制
int main()
{// 创建命名管道bool r = createFifo(NAMED_PIPE);assert(r);(void)r;std::cout << "sever begin" << std::endl;int rfd = open(NAMED_PIPE, O_RDONLY);std::cout << "sever end" << std::endl;if (rfd < 0)exit(1);// readchar buffer[1024];while (true){ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::cout << "client->server#" << buffer << std::endl;}else if (s == 0){std::cout << "client quit,me too!" << std::endl;break;}else{std::cout << "err string: " << strerror(errno) << std::endl;break;}}close(rfd);// 等待10秒 观察当前路径下的命名管道是否删除// sleep(10);removeFifo(NAMED_PIPE);return 0;
}
细节1:
默认情况下 读取进程先运行,只有当写入打开后,读取进程才会继续往后进行
read进程会卡在open函数这, 因为此时管道文件只有读描述符,没有写入描述符(读没有意义,所以阻塞)
相关文章:
【Linux】17. 进程间通信 --- 管道
1. 什么是进程间通信(进程间通信的目的) 数据传输:一个进程需要将它的数据发送给另一个进程 资源共享:多个进程之间共享同样的资源。 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了…...
有哪些有效的复习方法可以帮助备考软考?
软考目前仍然是一个以记忆为主、理解为辅的考试。学过软考的朋友可能会感到困惑,因为软考的知识在日常工作中有许多应用场景,需要理解的地方也很多。但为什么我说它是理解为辅呢?因为这些知识点只要记住了,都不难理解,…...
【MySQL | 第九篇】重新认识MySQL锁
文章目录 9.重新认识MySQL锁9.1MySQL锁概述9.2锁分类9.2.1锁的粒度9.2.2锁的区间9.2.3锁的性能9.2.4锁的级别 9.3拓展:意向锁9.3.1意向锁概述9.3.2意向锁分类9.3.3意向锁作用(1)意向锁的兼容互斥性(2)例子1(…...
含义:理财风险等级R1、R2、R3、R4、R5
理财风险等级R1、R2、R3代表什么,为什么R1不保本,R2可能亏损 不尔聊投资https://author.baidu.com/home?frombjh_article&app_id1704141696580953 我们购买理财产品的时候,首先都会看到相关产品的风险等级。风险等级约定俗成有5级&…...
ICode国际青少年编程竞赛- Python-2级训练场-列表入门
ICode国际青少年编程竞赛- Python-2级训练场-列表入门 1、 Dev.step(3)2、 Flyer.step(1) Dev.step(-2)3、 Flyer.step(1) Spaceship.step(7)4、 Flyer.step(5) Dev.turnRight() Dev.step(5) Dev.turnLeft() Dev.step(3) Dev.turnLeft() Dev.step(7) Dev.turnLeft() Dev.…...
【设计模式】14、strategy 策略模式
文章目录 十四、strategy 策略模式14.1 map_app14.1.1 map_app_test.go14.1.2 map_app.go14.1.3 navigate_strategy.go 十四、strategy 策略模式 https://refactoringguru.cn/design-patterns/strategy 需求: client 知道很多不同的策略, 希望在运行时切换. 场景示例: 就像高…...
C++类和对象(基础篇)
前言: 其实任何东西,只要你想学,没人能挡得住你,而且其实学的也很快。那么本篇开始学习类和对象(C的,由于作者有Java基础,可能有些东西过得很快)。 struct在C中的含义: …...
Oracle导入数据中文乱码问题处理,修改客户端字符编码跟数据库的一致
前提:SQL文件打开其中中文字符是正常显示,保证导出文件中文字符正常。通过sqlplus命令导入SQL文件出现乱码,这是因为客户端跟数据库的字符集不一致导致出现乱码问题。 要SQL导入的中文正常,要确保执行导入命令的客户端字符编码跟…...
【与 Apollo 共创生态:展望自动驾驶全新未来】
1、引言 历经七年的不懈追求与创新,Apollo开放平台已陆续推出了13个版本,汇聚了来自全球170多个国家与地区的16万名开发者及220多家合作伙伴。随着Apollo开放平台的不断创新与发展,Apollo在2024年4月19日迎来了Apollo开放平台的七周年大会&a…...
【webrtc】MessageHandler 5: 基于线程的消息处理:以PeerConnection信令线程为例
peerconn的信令是通过post 消息到自己的信令线程消息来处理的PeerConnectionMessageHandler 是具体的处理器G:\CDN\rtcCli\m98\src\pc\peer_connection_message_handler.hMachinery for handling messages posted to oneself PeerConnectionMessageHandler 明确服务于 signalin…...
计算机网络 3.2网络体系结构
第二节 网络体系结构 一、网络协议 1.定义: ①通信双方共同遵守的规则。 ②为网络数据交换制定的规则、约定与标准。 ③网络实体之间通信时有关信息传输顺序、信息格式、信息内容的约定或规则。 2.协议三要素: 语法:确定协议元素的格式…...
连接HiveMQ代理器实现MQTT协议传输
先下载MQTTX: MQTTX: Your All-in-one MQTT Client Toolbox 使用线上免费的MQTTX BROKER:The Free Global Public MQTT Broker | Try Now | EMQ 打开MQTTX,创建连接,点击NEW SUBSCRIPTION,创建一个主题,这里使用test/topic,在下面Json中填写…...
springcloud报错:Failed to start bean‘webServerStartStop‘
如果你正在使用nacos进行服务注册,然后报一下错误: 那就说明的nacos没有打开,所以找到你的下载nacos的文件夹 好了,错误完美解决~...
el-checkbox 无法动态设置勾选状态
问题 cheked 值动态变化,但是勾选状态无法动态改变 解决 v-model 与:checked 同时使用 <el-checkbox class"add-shop-check" v-model"renderData[0].isCheck" :checked"renderData[0].isCheck" change"checked > selec…...
车规级低功耗汽车用晶振SG-9101CGA
车规级晶振SG-9101CGA属于爱普生9101系列,是一款可编程晶振。SG-9101CGA车规级晶振采用2.5x2.0mm封装,利用PLL技术生产,此款振荡器的频率范围从0.67M~170MHZ任一频点可选,步进1ppm,采用标准CMOS输出,最大输…...
企业是保留传统的MES还是换新的MES?
在选择上MES系统的时候,企业可以根据自身所处行业不同、当前阶段不同,以及业务需求的差异,对症下药,选择适合自己的解决方案。对于有些企业本来就有MES系统,但是已经过时过旧,就要考虑换新的MES系统了. 保留…...
2024年第六届世界软件工程研讨会(WSSE 2024)即将召开!
2024年第六届世界软件工程研讨会(WSSE 2024)将于2024年9月13-15日在日本京都举行。软件工程领域的发展离不开各位专家学者和业界精英的共同努力和贡献。WSSE 2024将就软件工程领域的最新研究成果、实践经验和发展趋势进行深入交流和探讨,汇聚…...
Linux网络编程:TCP编程实现
目录 1、前言 2、函数介绍 2.1 socket函数 与 通信域 2.2 bind函数 与 通信结构体 2.2.1 domain通信地址族 与 通信结构体 2.2.2 IPv4地址族结构体 2.2.3 通用地址族结构体 2.2.4 示例:为套接字fd绑定通信结构体addr 2.3 listen函数 与 accept函数 …...
小剧场短剧影视小程序源码_后端PHP
项目运行截图 源码贡献 https://githubs.xyz/boot?app42 部署说明 linux/win任选 PHP版本:7.3/7.2(测试时我用的7.2要安装sg扩展 ) 批量替换域名http://video.owoii.com更换为你的 批量替换域名http://120.79.77.163:1更换为你的 这两个…...
C语言总结三:数组(压缩版)
一,数组概念 定义:相同类型元素的集合 二,一维数组 1,语法:type arr_name[常量值]; 2,初始化:int arr[5]{1,2,3,4,5}; 3,类型:int [5] 4,使用࿱…...
我独自升级崛起怎么玩 我独自升级崛起游玩教程分享
《我独自升级:ARISE》是一款预计在 Android、iOS 和 PC 平台推出的动作 RPG,故事内容基于网络漫画版本改编,讲述世界各地出现「次元传送门」,而少部分人类觉醒了可以对抗传送门中怪物的「猎人」能力,玩家可以在故事模式…...
前端上传大文件
在前端实现大文件上传,通常涉及以下几个关键步骤和技术要点,以确保上传过程既高效又稳定: 1. 文件切片 目的:将大文件分割成多个小块,以减少单次请求的负担,提高上传速度,并且增强上传的稳定性…...
Kompas AI图片转换器:高效解决格式不兼容问题
最新Kompas AI:一键转换图片格式,提升工作效率 在数字化的世界里,图片已成为我们交流和分享信息不可或缺的媒介。然而,不同的场景往往需要不同格式的图片,这时,一个高效的图片格式转换工具就显得尤为关键。…...
自动驾驶规划与控制技术解析
目录 1. 自动驾驶技术 2.定位location 3. 地图HD Map 4 预测prediction 5 自动驾驶路径规划 6. 自动驾驶路径规划 7. 规划planning 8. 视频路径 1. 自动驾驶技术 2.定位location 3. 地图HD Map 4 预测prediction 5 自动驾驶路径规划 6. 自动驾驶路径规划 7. 规划…...
计算机等级考试常见问题
目录 计算机二级报什么好? 计算机等级考试可以直接考4级吗 计算机等级考试包括什么...
C语言实战项目--贪吃蛇
贪吃蛇是久负盛名的游戏之一,它也和俄罗斯⽅块,扫雷等游戏位列经典游戏的行列。在编程语言的教学中,我们以贪吃蛇为例,从设计到代码实现来提升大家的编程能⼒和逻辑能⼒。 在本篇讲解中,我们会看到很多陌生的知识&…...
【LAMMPS学习】八、基础知识(5.3)Body particles体粒子
8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语,以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…...
【3D目标检测】常见相关指标说明
一、mAP指标 mean Average Precision(平均精度均值),它是目标检测和信息检索等任务中的重要性能指标。mAP 通过综合考虑精度和召回率来衡量模型的总体性能。 1.1 精度(Precision) 表示检索到的目标中实际为正确目标…...
QT设计模式:工厂模式
基本概念 工厂模式是一种创建型设计模式,用于将对象的创建逻辑与使用者分离,以实现对象的创建和使用的解耦。工厂模式提供了一个统一的接口来创建对象,而客户端代码只需通过该接口来请求所需的对象,而不需要知道具体的对象创建细…...
【电路笔记】-容抗
容抗 文章目录 容抗1、概述2、容抗示例13、容抗示例 24、容抗示例 35、分压器修订6、总结1、概述 容抗是电容器的复阻抗,其值随应用频率而变化。 与不依赖于频率的电阻不同,在交流电路中,电抗受电源频率的影响,并且其行为方式与电阻类似,两者都以欧姆为单位测量。 电抗会…...
wordpress调用文章代码/上海已经开始二次感染了
项目背景和意义 目的:本课题主要目标是设计并能够实现一个基于微信小程序景区景点预约购票系统,前台用户使用小程序,后台管理使用JavaMysql开发,后台使用了springboot框架;通过后台添加景区资讯、景点介绍,…...
如何自己做软件网站/域名交易域名出售
作业地址 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882 GIT仓库地址https://github.com/pxr000/pxxr/issues/1讨论画面: 1.PSP表格 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时…...
如何分析企业网站/免费外链发布平台在线
如果再不写点东西,害怕自己以后再也不会写程序了,很久没有更新BLOG了,作为即将失去九月的几年还是摘录一篇稿子中的东西吧——关于Command对象的,主要是关于参数的问题,Command对象参数输入输出,以及获取的…...
免费网站后台管理系统html/seo网站优化专员
在Hadoop生态圈中,针对大数据进行批量计算时,通常需要一个或者多个MapReduce作业来完成,但这种批量计算方式是满足不了对实时性要求高的场景。 Storm是一个开源分布式实时计算系统,它可以实时可靠地处理流数据。 Storm特点 在…...
珠海做网站推广公司/如何写推广软文
我正在通过Java创建几个XML文件,到目前为止一切正常,但是现在我在尝试创建一个带有命名空间前缀节点的文件时遇到了问题,即像< tns:node>这样的东西. …< / tns:node>使用我的代码的重构版本,该代码已经用于没有名称空间的普通xml文件.抛出的…...
网站如何收录/网红推广团队去哪里找
MATLAB同其他高级语言一致,有三种基本程序结构: 顺序结构;选择结构;循环结构 MATLAB流程控制语句主要有: ForWhileif-else-endswitch-case 常用命令: BreakContinue以及matlab特有的try命令࿰…...