Linux:进程间通信
目录
进程间通信目的
进程间通信分类
管道
System V IPC
POSIX IPC
什么是管道
站在文件描述符角度-深度理解管道
管道使用
管道通信的四种情况
管道通信的特点
进程池管理
命名管道
创建一个命名管道
命名管道的打开规则
命名管道通信实例
匿名管道与命名管道的区别
system V 共享内存
共享内存原理
共享内存函数
ftok函数
shmget函数
shmat函数
shmdt函数
shmctl函数
注意点
接口使用练习
system V 消息队列和信号量(仅仅了解)
消息队列
信号量
进程间通信目的
数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变
进程间通信分类
进程之间通信的区域是由第三方提供的,需要被进程看到同一块公共资源,通信种类的本质就是公共资源是由哪个模块提供的。
管道
匿名管道pipe
命名管道
System V IPC
System V 消息队列
System V 共享内存
System V 信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
什么是管道
管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
管道文件是一个内存级文件(即只存在于内存中,没有对外设IO的过程)。
站在文件描述符角度-深度理解管道
管道使用
#include<cstdio>
#include<cassert>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<iostream>
#include<sys/wait.h>
#include<cstring>
using namespace std;int main()
{//1.创建管道,打开读写端//pfd[o] 读端//pfd[1] 写端int pfd[2];int n = pipe(pfd);assert(n == 0);//2.创建子进程int id = fork();assert(id >= 0);if(id == 0){//子进程程序close(pfd[0]);int cnt = 0;char send[1024];while(true){snprintf(send, 1024,"子进程: %p send -> pipe count = %d",getpid(),cnt++);write(pfd[1], send, strlen(send));sleep(3);}exit(1);}//父进程程序close(pfd[1]);char receive[1024];while(true){int rn = read(pfd[0], receive, sizeof(receive) - 1);receive[rn] = 0;printf("receive : %s\n", receive);}waitpid(id, nullptr, 0);return 0;
}
管道通信的四种情况
1.如果管道无数据,读端阻塞
2.如果管道文件写满了,写端阻塞
3.管道的写端都关闭,读端退出阻塞状态,返回读到0个字节
4.管道读端关闭,OS发送13信号杀死写端进程
管道通信的特点
1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
2.管道提供流式服务
3.一般而言,进程退出,管道释放,所以管道的生命周期随进程
4.一般而言,内核会对管道操作进行同步与互斥
5.管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
进程池管理
#include<cstdio>
#include<cassert>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<iostream>
#include<sys/wait.h>
#include<cstring>
#include<vector>
#include<ctime>
using namespace std;
#define SUB_NUM 5//管理
struct subEp
{subEp(int fd, pid_t pid):_writefd(fd),_pid(pid){}int _writefd;pid_t _pid;
};typedef void(*func_p)();void task0()
{cout << "test0" << endl;sleep(1);
}void task1()
{cout << "test1" << endl;sleep(1);
}void task2()
{cout << "test2" << endl;sleep(1);
}void task3()
{cout << "test3" << endl;sleep(1);
}void task4()
{cout << "test4" << endl;sleep(4);
}//载入任务
void LoadTask(vector<func_p>& f)
{f.push_back(task0);f.push_back(task1);f.push_back(task2);f.push_back(task3);f.push_back(task4);
}int main()
{//管理不同进程vector<subEp> subs;//载入任务vector<func_p> funcs;LoadTask(funcs);//随机种子srand(time(nullptr));//创建子进程和管道for(int i = 0; i < SUB_NUM; ++i){int fd[2];int pipe_ret = pipe(fd);assert(pipe_ret == 0);pid_t pid = fork();assert(pid >= 0);if(pid == 0){close(fd[1]);//子进程读取int read_buffer;while(true){int rn = read(fd[0], &read_buffer, sizeof(read_buffer));assert(rn == 4 || rn == 0);if(rn == 0)break;if(read_buffer >= 0 && read_buffer < funcs.size()){cout << getpid() << " get masege -> " << endl;funcs[read_buffer]();}else{cout << getpid() << " get error masege !!!!" << endl;}}exit(1);}close(fd[0]);subs.push_back(subEp(fd[1], pid));}//父进程发送int cnt = 5;while(cnt--){int proc_code = rand() % subs.size();int task_code = rand() % funcs.size(); int wn = write(subs[proc_code]._writefd, &task_code, sizeof(int));assert(wn == 4);sleep(2);}//关闭父进程写端for(int i = 0; i < subs.size(); ++i){close(subs[i]._writefd);}//接收释放子进程cout << endl;cout << endl;for(int i = 0; i < subs.size(); ++i){pid_t waitid = waitpid(subs[i]._pid, nullptr, 0);assert(waitid > 0);cout << waitid << " has been received success ......." << endl;}return 0;
}
但是上面的代码有一个小问题(虽然不影响使用):子进程继承了父进程的写入端
将代码创建子进程和管道部分改成下列这样就可以了:
//创建子进程和管道std::vector<int> deleteFd;for(int i = 0; i < SUB_NUM; ++i){int fd[2];int pipe_ret = pipe(fd);assert(pipe_ret == 0);pid_t pid = fork();assert(pid >= 0);if(pid == 0){for(int i = 0; i < deleteFd.size(); i++) close(deleteFd[i]);close(fd[1]);//子进程读取int read_buffer;while(true){int rn = read(fd[0], &read_buffer, sizeof(read_buffer));assert(rn == 4 || rn == 0);if(rn == 0)break;if(read_buffer >= 0 && read_buffer < funcs.size()){cout << getpid() << " get masege -> " << endl;funcs[read_buffer]();}else{cout << getpid() << " get error masege !!!!" << endl;}}exit(1);}close(fd[0]);subs.push_back(subEp(fd[1], pid));deleteFd.push_back(fd[1]);}
命名管道
管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件(管道文件)
创建一个命名管道
命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
mkfifo filename
命名管道也可以从程序里创建,相关函数有:
移除管道文件使用unlink函数:
命名管道的打开规则
如果当前打开操作是为读而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
O_NONBLOCK enable:立刻返回成功
如果当前打开操作是为写而打开FIFO时
O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
命名管道通信实例
#include"comm.hpp"int main()
{//创建命名管道文件umask(0);int ret_fifo = mkfifo(FIFONAME, 0600);assert(ret_fifo == 0);//发送信息int wfd = open(FIFONAME, O_WRONLY);assert(wfd >= 0);char write_buffer[1024];while(true){cout << "server -> client : ";fgets(write_buffer, sizeof(write_buffer) - 1, stdin);write_buffer[strlen(write_buffer) - 1] = 0;ssize_t wr = write(wfd, write_buffer, strlen(write_buffer));assert(wr >= 0);}close(wfd);return 0;
}
#include"comm.hpp"int main()
{//创建命名管道文件umask(0);int ret_fifo = mkfifo(FIFONAME, 0600);assert(ret_fifo == 0);//发送信息int wfd = open(FIFONAME, O_WRONLY);assert(wfd >= 0);char write_buffer[1024];while(true){cout << "server -> client : ";fgets(write_buffer, sizeof(write_buffer) - 1, stdin);write_buffer[strlen(write_buffer) - 1] = 0;ssize_t wr = write(wfd, write_buffer, strlen(write_buffer));assert(wr >= 0);}close(wfd);return 0;
}
#include"comm.hpp"int main()
{//接收信息int rfd = open(FIFONAME, O_RDONLY);assert(rfd >= 0);char read_buffer[1024];while(true){ssize_t rn = read(rfd, read_buffer, sizeof(read_buffer) - 1);if(rn > 0){read_buffer[rn] = 0;cout << read_buffer << endl;}else if(rn == 0){cout << " quit " << endl;break;}else{cout << " error " << endl;break;}}close(rfd);//移除命名管道文件unlink(FIFONAME);return 0;
}
注意:如果一个管道文件已经存在,重复创建会失败。
匿名管道与命名管道的区别
匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用open FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完 成之后,它们具有相同的语义。
system V 共享内存
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
共享内存原理
原本进程之间是独立的,两个进程的虚拟地址空间是不能指向同一块物理内存的。但是进程通信的本质就是让不同的进程看到同一份资源,在共享内存中这个同一份资源指的就是内存。也就是通过调用系统接口,使得不同的进程的虚拟地址空间可以映射到同一块物理内存。
实现的步骤如下:
1.申请一块物理内存空间,使用shmget(shared memmery get)
2.将创建好的物理内存与进程的进程地址空间相映射(挂接),shmat(attach)
3.若是不想通信了
a.取消进程地址与共享内存的映射关系(去关联), shmdt
b.释放内存 , shmctl
共享内存函数
ftok函数
将路径和项目id生成一个独一无二的标识。
shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
IPC_CREAT : 如果不存在则创建,如果存在则获取
IPC_EXCL :不能单独使用,IPC_CREAT | IPC_EXCL 如果不存在则创建,如果存在则返回错误码
shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
shmdt函数
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1 注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1
注意点
1.共享内存也要先描述再组织,共享内存 = 物理内存块 + 共享内存相关属性(结构体储存,内含key)
2.key是为了保证创建的共享内存再OS中是唯一的。
3.shmid与key的关系犹如fd与inode一样,一个是用户接口使用,一个是OS中使用,且前一个指向的结构体中包含后一个。
4.命令行查看和删除如下:
5.要挂接,在创建时就要加上权限
6.共享内存的有点是减少了数据的拷贝次数,通信速度块。
7.共享内存的缺点是没有同步和互斥,没有对数据的保护。
接口使用练习
shm_com.hpp
#include<cstdio>
#include<iostream>
#include<unistd.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<cerrno>
using namespace std;#define PATHNAME "."
#define MAXSIZE 4096//获取key
key_t getKey()
{key_t key = ftok(PATHNAME, 0x6666);if(key == -1){perror("getKey : ");exit(1);}return key;
}//创建共享内存
int creatShm(key_t key)
{int shmid = shmget(key, MAXSIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){perror("creatShm :");exit(2);}return shmid;
}//获取共享内存
int getShm(key_t key)
{int shmid = shmget(key, MAXSIZE, IPC_CREAT);if(shmid == -1){perror("getShm :");exit(3);}return shmid;
}//挂接
void* attachShm(int shmid)
{void* n = shmat(shmid, nullptr, 0);if(n == (void*)-1){perror("attachShm :");}return n;
}//去关联
void detachShm(const void* buffer)
{int n = shmdt(buffer);if(n == -1){perror("attachShm :");}
}//释放内存
void delShm(int shmid)
{if(-1 == shmctl(shmid, IPC_RMID, nullptr)){perror("delShm : ");exit(4);}
}
server.cc
#include"shm_com.hpp"int main()
{int key = getKey();int shmid = creatShm(key);void* shm_buffer = attachShm(shmid);int cnt = 20;while(--cnt){sleep(2);snprintf((char*)shm_buffer, MAXSIZE, "server -> client : pid : %d , cnt : %d\n", getpid(), cnt);}detachShm(shm_buffer);delShm(shmid);return 0;
}
client.cc
#include"shm_com.hpp"int main()
{int key = getKey();int shmid = getShm(key);void* shm_buffer = attachShm(shmid);while(true){sleep(2);printf("%s", (char*) shm_buffer);}detachShm(shm_buffer);return 0;
}
system V 消息队列和信号量(仅仅了解)
消息队列
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法
每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值
特性方面 IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期随内核
系统调用接口:
信号量
信号量的本质是计数器,通常用于表示公共资源中资源数目的多少。
信号量的意义是为了保护公共资源,防止数据不一致等问题。
公共资源: 被多人进程同时可以访问的资源
访问没有保护的公共资源: 数据不一致问题(并发访问冲突)要保护公共资源的逻辑推导:
为什么要让不同的进程看到同一份资源呢? 因为我想通信,进程间实现协同->但是进程有独立性 -> 为了让进程看到同一份资源 -> 提出方法 ->但引入了新的问题(数据不一致问题)->我们未来将被保护起来的公共资源: 临界资源(被保护的公共资源)资源(内存,文件,网络等)是要被使用的,如何被进程使用呢?一定是该进程有对应的代码来访问这部分临界资源:调用临界资源的代码属于临界区,访问独立资源的代码非临界区
信号量主要用于同步和互斥的,下面先来看看什么是同步和互斥。
如何保护公共资源: 互斥&&同步
原子性:要么不做,要做就做完,两态的这种情况
共享资源:
1.作为一个整体使用
2.划分成为一个一个的资源子部分
当我们想要某种资源的时候,我们可以进行预订
如果一个信号量初始值: 1
二元信号量 -- 互斥功能
所有的进程在访问公共资源之前,都必须先申请sem信号量 ->必须先申请sem信号量的前提,是所有进程必须先得看到同一个信号量 ->信号量本身就是公共资源 ->信号量是不是也要保证自己的安全呢? --,++(PV) ->信号量必须保证自身操作的安全性,--,++操作是原子态的!!
系统调用接口:
semget
semctl
semop
相关文章:
Linux:进程间通信
目录 进程间通信目的 进程间通信分类 管道 System V IPC POSIX IPC 什么是管道 站在文件描述符角度-深度理解管道 管道使用 管道通信的四种情况 管道通信的特点 进程池管理 命名管道 创建一个命名管道 命名管道的打开规则 命名管道通信实例 匿名管道与命名管道的…...
【java】将LAC改造成Elasticsearch分词插件
目录 为什么要将LAC改造成ES插件? 怎么将LAC改造成ES插件? 确认LAC java接口能work 搭建ES插件开发调试环境 编写插件 生成插件 安装、运行插件 linux版本的动态链接库生成 总结 参考文档 为什么要将LAC改造成ES插件? ES是著名的非…...
TPM 2.0实例探索3 —— LUKS磁盘加密(5)
接前文:TPM 2.0实例探索3 —— LUKS磁盘加密(4) 本文大部分内容参考: Code Sample: Protecting secret data and keys using Intel Platform... 二、LUKS磁盘加密实例 4. 将密码存储于TPM的PCR 现在将TPM非易失性存储器中保护…...
mybatisplus复习(黑马)
学习目标能够基于MyBatisPlus完成标准Dao开发能够掌握MyBatisPlus的条件查询能够掌握MyBatisPlus的字段映射与表名映射能够掌握id生成策略控制能够理解代码生成器的相关配置一、MyBatisPlus简介MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工…...
【数据聚类|深度聚类】Deep Comprehensive Correlation Mining for Image Clustering(DCCM)论文研读
Abstract 翻译 最近出现的深度无监督方法使我们能够联合学习表示和对未标记数据进行聚类。这些深度聚类方法主要关注样本之间的相关性,例如选择高精度对来逐步调整特征表示,而忽略了其他有用的相关性。本文提出了一种新的聚类框架,称为深度全面相关挖掘(DCCM),从三个方面…...
CE认证机构有哪些机构?
CE认证机构有哪些机构? 所有出口欧盟的产品都需要办理CE证明,而电子电器以及玩具是强制性要做CE认证。很多人以为只有办理欧盟NB公告机构的CE认证才可以被承认,实际上并不是。那么,除了NB公告上的机构,还有哪些认证机…...
MYSQL5.7:Access denied for user ‘root‘@‘localhost‘ (using password:YES)解决方法
一、打开MySQL目录下的my.ini文件,在文件的[mysqld]下面添加一行 skip-grant-tables,保存并关闭文件;skip-grant-tables :跳过密码登录,登录时无需密码。my.ini :一般在和bin同目录下,如果没有的话可自己创…...
单目运算符、双目运算符、三目运算符
单目运算符是什么 单目运算符是指运算所需变量为一个的运算符 又叫一元运算符,其中有逻辑非运算符:!、按位取 反运算符:~、自增自减运算符:,-等。 逻辑非运算符【!】、按位取反运算符【~】、 自…...
离线数据仓库项目搭建——准备篇
文章目录(一)什么是数据仓库(二)数据仓库基础知识(三)数据仓库建模方式(1)星行模型(2)雪花模型(3)星型模型 VS 雪花模型(四…...
十七、本地方法接口的理解
什么是本地方法? 1.简单来讲,一个Ntive method 就是一个Java调用非Java代码的接口.一个Native Method 是这样一个Java方法:该方法的实现由非Java语言实现,比如C,这个特征并非Java所特有,很多其他的编程语言都由这一机制,比如在C中…...
【halcon】模板匹配参数之金字塔级数
背景 今天,在使用模板匹配的时候,突然程序卡死,CPU直接飙到100%。最后排查发现是模板匹配其中一个参数 NumLevels 导致的: NumLevels: The number of pyramid levels used during the search is determined with numLevels. If n…...
jupyter lab安装和配置
jupyter lab 安装和配置 一、jupyter lab安装并配置 安装jupyterlab pip install jupyterlab启动 Jupyter lab默认会打开实验环境的,也可以自己在浏览器地址栏输入127.0.0.1:8888/lab 汉化 pip install jupyterlab-language-pack-zh-CN刷新一下网页࿰…...
用Docker搭建yolov5开发环境
拉取镜像 sudo docker pull pytorch/pytorch:latest 创建容器 sudo docker run -it -d --gpus "device0" pytorch/pytorch bash 查看所有容器 sudo docker ps -a 查看运行中的容器 sudo docker ps 进入容器 docker start -i 容器ID 将依赖包全都导入到requiremen…...
Apache Pulsar 云原生消息中间件之王
一、简介 pulsar,消息中间件,是一个用于服务器到服务器的消息系统,具有多租户、高性能等优势。 pulsar采用发布-订阅的设计模式,producer发布消息到topic,consumer订阅这些topic处理流入的消息,并当处理完…...
精选博客系列|公用事业中的VMware:在边缘重新定义价值
VMware 已经成为公用事业行业的核心。您可以在那里找到例如 VMware vSphere(包括基础 Hypervisor ESXi 和 VMware vCenter 建立的整体控制平面)的核心产品。来自软件定义的基础架构带来的诸多好处使 IT 团队将其先前基于硬件的系统转变为 VMware Cloud F…...
数字档案室测评的些许感悟
我是甲方,明明我家是档案“室”,为什么申请的是数字档案“馆”? 笔者正对着手里的一份方案苦笑,甲方爸爸是某机关单位档案室,方案最后的附件赫然写着几个大字:“申请国家级数字档案馆……“。这样的事屡见…...
Java 函数式编程实例
一、函数式编程概念 函数式编程是一种编程的范式和编程的方法论(programming paradigm),它属于结构化编程的一种,主要的思想是把运算的过程尽量通过一组嵌套的函数来实现。 函数式编程的几个特点: 函数可以作为变量、参数、返回值和数据类…...
Ant design Chart onReady函数使用外部变量问题
一、问题描述封装了一个Chart组件,它接收一个boolean类型的props,根据这个boolean的true或false执行不同的操作。经过console.log验证,onReady函数只会在组件初次渲染时取到props值,不管后面的props变化成什么都无法重新取值。二、…...
Unity使用webSocket与服务器通信(一)搭建一个简单地服务器和客户端
你想在unity WebGL里面使用TCP通信吗,那么你可以用一用webSocket。当然,桌面端也可以使用webSocket,这样Unity多平台发布的时候,业务层的通信代码可以使用一套,而不是桌面用socket,网页用http… 一、什么是…...
SpringCloud微服务实战——搭建企业级开发框架(四十九):数据字典注解的设计与实现
数据字典是系统中基本的必不可少的功能,在多种多样的系统中,数据字典表的设计都大同小异。但是使用方式确是多种多样,设计好一套易用的数据字典功能模块,可以使开发事半功倍。 常用的数据字典使用方式: 直接在SQL语句…...
mysql下,实现保存指定用户、ip、命令的查询日志
环境:mysql 8.0.14 社区版 阅读文本需要的背景知识:对数据库的基本概念(触发器、存储过程、事件),mysql下general log的配置指令 背景:因审计需要,对于数据库操作需要留痕。实际访问数据库的有…...
Vue 3.0 学习笔记之基础知识
系列文章目录 提示:阅读本章之前,请先阅读目录 文章目录系列文章目录前言Vue 3.0 创建与Vue2.0对比的变化关闭语法检查setup 组合式函数compositions响应式数据 refreactive 函数Vue3.0 响应原理ref 和 reactive 区别setup 注意点computed 计算函数watch…...
WebGIS行政区炫酷特效——流光特效教程
先来看下效果: 图片截图: 流光特效的思路是从行政区的边界中随着时间不断的取若干段线条换成另一种高亮颜色。 流光的第一步首先是发光,发光的教程在这里: GIS矢量图形多边形地块行政区发光,阴影发光特效实现_疯狂的GISer的博客-CSDN博客 学会发光以后,接下来需要做的…...
2023-3-3 刷题情况
保证文件名唯一 题目描述 给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹:在第 i 分钟,新建名为 names[i] 的文件夹。 由于两个文件 不能 共享相同的文件名,因此如果新建文件夹使用的文件名已经被占用…...
《青浦区加快发展跨境电子商务实施细则(审议稿)》
为进一步贯彻落实《中华人民共和国电子商务法》,上海市《关于促进本市跨境电子商务发展的若干意见》,切实做好青浦区跨境电子商务试点工作,探索和规范跨境电子商务管理,促进跨境电子商务健康快速发展,青浦商务委根据多…...
【React全家桶】React生命周期
React生命周期 1、初始化阶段 componentDidMount:render之前最后一次修改状态的机会 render:只能访问this.props和this.state,不允许修改状态和DOM输出 componentDidMount:成功render并渲染完成真实DOM之后触发 2、旧生命周期 👉👉👉加…...
B. Count the Number of Pairs
原题链接 纯纯水一下; 昨天晚上的比赛,由于半夜打的,精神状态不好,wa了俩发直接睡觉去了,现在白天写写发现,不难,水中水 模拟题吧,题目怎么说就这么作 Kristina has a string ss…...
离线数据仓库项目--技术选择
文章目录(一)技术选型1)数据采集工具2)数据存储3)数据计算4)数据可视化(二)整体架构设计(三)服务器资源规划(一)技术选型 1ÿ…...
GC Garbage Collectors
本质一、算法1、哪些是垃圾?引用计数法:reference countPython中使用了。个对象如果没有任何与之关联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。漏洞:循环…...
【网络】-- 网络基础
(本文是网络的宏观的概念铺垫) 目录 计算机网络背景 网络发展 认识 "协议" 网络协议初识 协议分层 OSI七层模型 TCP/IP 五层(或四层)模型 报头 以太网 碰撞 路由器 IP地址和MAC地址 IP地址与MAC地址总结 IP地址 MAC地址 计算机…...
医院网站建设运行管理办法/以下哪个单词表示搜索引擎优化
flume采集数据 kafka做消息队列(缓存) storm做流式处理 flume版本 apache-flume-1.7.0-bin kafka版本 kafka_2.11-0.10.1.0(要注意的是有些flume的版本和kafka的版本不兼容,flume采集的数据无法写入到kafka的话题中去ÿ…...
如何把自己做的网站挂网上/优秀营销软文范例800字
1、背景 在离线环境下(局域网中)的GIS系统中如何使用地图?这里的地图主要指的是地图底图,有了底图切片数据,我们就可以看到地图,在上面加上自己的业务数据图层,进行相关操作。 要在离线环境下看…...
vi设计案例ppt/网络推广优化培训
第一部分 Javascript弹出子窗口 可以通过多种方式实现,下面介绍几种方法 (1) 通过window对象的open()方法,open()方法将会产生一个新的window窗口对象 window.open(URL,windowName,parameters); 也可以这样写: var newWindow open(,_blank)…...
运城做网站/长沙正规竞价优化服务
详见fhq blog 主要是先拓扑..然后构建新树 然后倍增求儿子 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int MAXN700000; struct EDGE {int tot,g[MAXN],nnext[MAXN],num[MAXN];void…...
万网建网站流程/东莞seo广告宣传
此环境搭建是OpenCV的python(一下简称py)开发环境搭建,建立在py3的环境和语法上实现的。 windows系统搭建 系统环境:windows 10 python 3.6 OpenCV 3.4.1 一、安装python python的安装之前在python自学笔记的项目中描述了&…...
浙江省建设厅网站张清云/查排名网站
wy的leetcode刷题记录_Day64 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间:2023-3-29 前言 目录wy的leetcode刷题记录_Day64声明前言1641. 统计字典序元音字符串的数目题目介绍思路代码收获1641. 统计字典序元音字符串的数目 今天的每…...