I/O多路复用三种实现
一.select 实现
(1)select流程
基本流程是:
1. 先构造一张有关文件描述符的表; fd_set readfds
2. 清空表 FD_ZERO()
3. 将你关心的文件描述符加入到这个表中; FD_SET()
4. 调用select函数。 selset()
5. 判断是哪一个或哪些文件描述符产生了事件(IO操作); FD_ISSET()
6. 做对应的逻辑处理;
(2)selset函数
头文件: #include<sys/select.h> #include<sys/time.h>
#include<sys/types.h> #include<unistd.h>
声明: int select(int nfds, fd_set *readfds, fd_set *writefds,\
fd_set *exceptfds, struct timeval *timeout);
功能:监测是哪些文件描述符产生事件,阻塞等待产生.
参数:nfds: 监测的最大文件描述个数(文件描述符从0开始,这里是个数,记得+1)
readfds: 读事件集合; // 键盘鼠标的输入,客户端连接都是读事件
writefds: 写事件集合; //NULL表示不关心
exceptfds:异常事件集合; //NULL 表示不关心
timeout: 设为NULL,等待直到某个文件描述符发生变化;
设为大于0的值,有描述符变化或超时时间到才返回。
超时时间检测:如果规定时间内未完成函数功能,返回一个超时的信息,我们可以根据该信息设定相应需求;
返回值: <0 出错 >0 表示有事件产生;
如果设置了超时检测时间:&tv
<0 出错 >0 表示有事件产生; ==0 表示超时时间已到;
结构体如下:
struct timeval {
long tv_sec; 以秒为单位,指定等待时间
long tv_usec; 以毫秒为单位,指定等待时间
};
void FD_CLR(int fd, fd_set *set); //将set集合中的fd清除掉
int FD_ISSET(int fd, fd_set *set); //判断fd是否在set集合中产生了事件
void FD_SET(int fd, fd_set *set); //将fd加入到集合中
void FD_ZERO(fd_set *set); //清空集合
(3)Select特点:
Select特点:
1. 一个进程最多只能监听1024个文件描述符 (32位) [64位为 2048]
2. select被唤醒之后要重新轮询(0-1023)一遍驱动,效率低(消耗CPU资源)
3. select每次会清空未响应的文件描述符,每次都需要拷贝用户空间的表到内核空间,效率低,开销较大
(0~3G是用户态,3G~4G是内核态,两个状态来回切换 拷贝是非常耗时,耗资源的)
(4)select机制:
1. 头文件检测1024个文件描述符 0-1023
2. 在select中0~2存储标准输入、标准输出、标准出错
3. 监测的最大文件描述个数为fd+1(如果fd = 3,则最大为 4) : //因为从0开始的
4. select只对置1的文件描述符感兴趣 ,假如事件产生,select检测时 , 产生的文件描述符会保持1,未产生事件的会置0;
5. select每次轮询都会清空表(置零的清空) //需要在select前备份临时表

练习1:
如何通过select实现 响应鼠标事件同时响应键盘事件?
代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, char const *argv[])
{int fd = open("/dev/input/mouse0", O_RDONLY);if (fd < 0){perror("open is err:");return -1;}//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(fd, &readfds);fd_set readfdcp = readfds;int maxfd = fd;char buf[128] = {0};while (1){//4.检测是否有相应select(maxfd + 1, &readfds, NULL, NULL, NULL);//5.检测哪一个文件描述符if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("key: %s\n", buf);}if (FD_ISSET(fd, &readfds)){int ret = read(fd, buf, sizeof(buf));buf[ret] = '\0';printf("mouse: %s\n", buf);}readfds = readfdcp;}return 0;
}
练习2:
select是文件描述符和下标一一对应,0只能对应0号文件描述符。因此只有最大的文件描述符关闭时,才--len。注意增加删除时是针对实际表,不是临时表。
使用select实现server的全双工
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建表fd_set readfds;//2/清空表FD_ZERO(&readfds);//3.设置表FD_SET(0, &readfds);FD_SET(sockfp, &readfds);fd_set readfdcp = readfds;int maxfd = sockfp;struct timeval st;while (1){readfds = readfdcp;//4.检测是否有响应st.tv_sec = 5;st.tv_usec = 0;int ret = select(maxfd + 1, &readfds, NULL, NULL, &st);if (ret < 0){perror("select err");return -1;}else if (ret == 0){printf("无响应\n");}//0响应,证明服务器要发送消息if (FD_ISSET(0, &readfds)){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';for (int i = 4; i <= maxfd; ++i){send(i, buf, sizeof(buf), 0);}}//sockfp,监听套接字响应证明,有客户端要链接if (FD_ISSET(sockfp, &readfds)){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d ip: %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));FD_SET(acceptfp, &readfdcp);if (acceptfp > maxfd)maxfd = acceptfp;}//检测客户端,检查是哪一个客户端发送的消息for (int i = 4; i <= maxfd; ++i){if (FD_ISSET(i, &readfds)){int recvbyte = recv(i, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(i);FD_CLR(i, &readfdcp);if (i == maxfd)--maxfd;}else{printf("%d : %s\n", i, buf);}}}}return 0;
}
练习3:
使用select实现client的全双工
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>int main(int argc, const char *argv[])
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd < 0){perror("socker is err:");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr(argv[2]);if(connect(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0){perror("connect is err:");return -1;}//1.创建表fd_set readfds,tempfds;//2.清空表FD_ZERO(&readfds);FD_ZERO(&tempfds);//3.添加文件描述符FD_SET(0,&readfds);FD_SET(sockfd,&readfds);int maxfd = sockfd;int ret;char buf[128];while(1){tempfds = readfds;//4.调select检测ret = select(maxfd+1,&tempfds,NULL,NULL,NULL);if(ret < 0){perror("select is err:");return -1;}if(FD_ISSET(0,&tempfds)){fgets(buf,sizeof(buf),stdin);if(buf[strlen(buf)-1] == '\n')buf[strlen(buf)-1] = '\0';send(sockfd,buf,sizeof(buf),0);}if(FD_ISSET(sockfd,&tempfds)){int recvbyte = recv(sockfd,buf,sizeof(buf),0);if(recvbyte < 0){perror("recv is err:");return -1;}printf("%s\n",buf);}}close(sockfd);return 0;
}
(5)select的超时时间检测:
超时检测的必要性:
1. 避免进程在没有数据时无限制的阻塞;
2. 规定时间未完成语句应有的功能,则会执行相关功能;
结构体如下:
struct timeval {
long tv_sec; 以秒为单位,指定等待时间
long tv_usec; 以毫秒为单位,指定等待时间
};
二.poll实现
(1)poll流程
使用: 1.先创建结构体数组 struct pollfd fds[100];
2.添加结构体成员的文件描述符以及触发方式 fds[0].fd = ?;fds[0].events = POLLIN
3.保存数组内最后一个有效元素的下标
4. 调用函数poll ret = poll(fds,nfds+1,-1);
5.判断结构体内文件描述符是否触发事件 fds[i].revents == POLLIN
6.根据不同的文件描述符触发不同事件
(2)poll函数
声明:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
头文件: #include<poll.h>
功能: 监视并等待多个文件描述符的属性变化
参数:
1.struct pollfd *fds: 关心的文件描述符数组,大小自己定义
若想检测的文件描述符较多,则建 立结构体数组struct pollfd fds[N];
struct pollfd
{
int fd; //文件描述符
short events; //等待的事件触发条件----POLLIN读时间触发(大多数)
short revents; //实际发生的事件(未产生事件: 0 ))
}
2. nfds: 最大文件描述符个数
3. timeout: 超时检测 (毫秒级):1000 == 1s
如果-1,阻塞 如果0,不阻塞
返回值: <0 出错 >0 表示有事件产生;
如果设置了超时检测时间:&tv
<0 出错 >0 表示有事件产生; ==0 表示超时时间已到;
(3)poll特点
1. 优化文件描述符个数的限制;
(根据poll函数第一个函数的参数来定,如果监听的事件为1个,则结构体数组容量为1,如果想监听100个,那么这个结构体数组的容量就为100,多少文件描述符由程序员自己来决定)
2. poll被唤醒之后需要重新轮询一遍驱动,效率比较低(消耗CPU)
3. poll不需重新构造文件描述符表(也不需清空表),只需要从用户空间向内核空间拷贝一次数据(效率相对比较高)
练习:
使用poll实现server的全双工
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建结构体数组struct pollfd fds[100];//2.添加文件描述符和触发方式fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = sockfp;fds[1].events = POLLIN;int nfds = 1;int ret;while (1){//3.poll轮循检测ret = poll(fds, nfds + 1, 2000);if (ret < 0){perror("poll is err");return -1;}else if (ret == 0){printf("qeqweqe\n");continue;}//4. 判断哪一个文件描述符产生响应,并发布任务for (int i = 0; i <= nfds; ++i){if (fds[i].revents == POLLIN){if (fds[i].fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';//printf("发送信息:\n");for (int j = 2; j <= nfds; ++j){send(fds[j].fd, buf, sizeof(buf), 0);}}else if (fds[i].fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d ip: %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));fds[++nfds].fd = acceptfp;fds[nfds].events = POLLIN;}else{int recvbyte = recv(fds[i].fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", i);close(fds[i].fd);//覆盖fds[i] = fds[nfds];//--i,--nfds后,最后一个循环不到--nfds, --i;}else{printf("%d : %s\n", i, buf);}}}}}return 0;
}
(4)poll超时时间检测
timeout: 超时检测 (毫秒级):1000 == 1s
如果-1,阻塞 如果0,不阻塞
三.epoll实现
(1)epoll流程:
Epoll的使用:
1.创建红黑树 和 就绪链表 int epfd = epoll_create(1);
2.添加文件描述符和事件信息到树上
event.events = EPOLLIN|EPOLLET;
event.data.fd = 0;
epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event
3.阻塞等待事件的产生,一旦产生事件,则进行处理
int ret = epoll_wait(epfd,events,32,-1);
4.根据链中准备处理的文件描述符 进行处理
(2)epoll函数族
epoll 要使用一组函数: epoll_create 创建红黑树 和 就序链表
epoll_ctl 添加文件描述符和事件到树上 / 从树上删除
epoll_wait 等待事件产生
epoll_create
创建红黑树以及链表
头文件:#include <sys/epoll.h>
声明:int epoll_create(int size);
功能:创建红黑树根节点(创建epoll实例) , 同时也会创建就绪链表
返回值:成功时返回一个实例(二叉树句柄),失败时返回-1。
epoll_ctl
控制epoll属性
声明: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:控制epoll属性,比如给红黑树添加节点
参数: 1. epfd: epoll_create函数的返回句柄。//一个标识符
2. op:表示动作类型,有三个宏:
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_MOD:修改已注册fd的监听事件
EPOLL_CTL_DEL:从epfd中删除一个fd
3. 要操作的文件描述符
4. 结构体信息:
typedef union epoll_data {
int fd; //要添加的文件描述符,只用这个
uint32_t u32; typedef unsigned int
uint64_t u64; typedef unsigned long int
} epoll_data_t;
struct epoll_event {
uint32_t events; 事件
epoll_data_t data; //共用体(看上面)
};
关于events事件:
EPOLLIN: 表示对应文件描述符可读
EPOLLOUT: 可写
EPOLLPRI:有紧急数据可读;
EPOLLERR:错误;
EPOLLHUP:被挂断;
EPOLLET:触发方式,边缘触发;(默认使用边缘触发)
ET模式:表示状态的变化;
NULL: 删除一个文件描述符使用,无事件
返回值:成功:0, 失败:-1
epoll_wait
等待事件产生
声明: int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件产生
内核会查找红黑树中有事件响应的文件描述符, 并将这些文件描述符放入就绪链表
就绪链表中的内容, 执行epoll_wait会同时复制到第二个参数events
参数: epfd:句柄;
events:用来保存从就绪链表中响应事件的集合;(传出参数,定义结构体数组)
maxevents: 表示每次在链表中拿取响应事件的个数;
timeout:超时时间,毫秒,0立即返回 ,-1阻塞
返回值: 成功: 实际从链表中拿出的数目 失败时返回-1
(4)epoll特点
1.监听的最大的文件描述符没有个数限制(取决与你自己的系统 1GB - 10万个左右)
2.异步I/O,epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高
3.epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.
(5)epoll机制
select,poll都属于 同步IO机制(轮询)
epoll属于异步IO机制(不轮询):
Epoll处理高并发,百万级
- 红黑树: 是特殊的二叉树(每个节点带有属性),Epoll怎样能监听很多个呢?首先创建树的根节点,每个节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,callback函数)
- 就绪链表: 当某一个文件描述符产生事件后,会自动调用callback函数,通过回调callback函数来找到链表对应的事件(读时间还是写事件)。

练习:
epoll实现server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>int acceptfp;
int main(int argc, char const *argv[])
{char buf[128] = {0};//1.创建套接字,返回建立链接的文件描述符int sockfp = socket(AF_INET, SOCK_STREAM, 0);if (sockfp == -1){perror("socket is err");exit(0);}printf("%d\n", sockfp);//2.绑定ip和端口号struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = inet_addr("0.0.0.0");socklen_t len = sizeof(struct sockaddr_in);if (bind(sockfp, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind is err");exit(0);}//3.listen监听if (listen(sockfp, 5)){perror("liste err");exit(0);}printf("listen ok\n");//1.创建红黑树以及链表//树的跟节点/树的句柄int epfd = epoll_create(1);//2.上树struct epoll_event event;struct epoll_event events[32] ;event.events = EPOLLET | EPOLLIN;event.data.fd = 0;epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);event.data.fd = sockfp;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfp, &event);while (1){//3.阻塞等待文件描述符产生事件int ret = epoll_wait(epfd, events, 32, -1);printf("asdsdfgdsf\n");if (ret < 0){perror("epoll err");return -1;}//4.根据文件描述符号,进行处理for (int i = 0; i < ret; ++i){if (events[i].data.fd == 0){fgets(buf, sizeof(buf), stdin);if (buf[strlen(buf) - 1] == '\n')buf[strlen(buf) - 1] = '\0';printf("发送信息:\n");//send(fds[j].fd, buf, sizeof(buf), 0);}else if (events[i].data.fd == sockfp){acceptfp = accept(sockfp, (struct sockaddr *)&caddr, &len);if (acceptfp < 0){perror("acceptfp");exit(0);}printf("port:%d ip: %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));//上树event.data.fd = acceptfp;epoll_ctl(epfd, EPOLL_CTL_ADD, acceptfp, &event);}else{int recvbyte = recv(events[i].data.fd, buf, sizeof(buf), 0);if (recvbyte < 0){perror("recv err");return -1;}else if (recvbyte == 0){printf("%d client is exit\n", events[i].data.fd);close(events[i].data.fd);//下树epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);}else{printf("%d : %s\n", events[i].data.fd, buf);}}}}return 0;
}
对比

相关文章:
I/O多路复用三种实现
一.select 实现 (1)select流程 基本流程是: 1. 先构造一张有关文件描述符的表; fd_set readfds 2. 清空表 FD_ZERO() 3. 将你关心的文件描述符加入到这…...
DataInputStream数据读取 Vs ByteBuffer数据读取的巨大性能差距
背景: 今天在查找一个序列化和反序列化相关的问题时,意外发现使用DataInputStream读取和ByteBuffer读取之间性能相差巨大,本文就来记录下这两者在读取整数类型时的性能差异,以便在平时使用的过程中引起注意 DataInputStream数据…...
org.apache.flink.table.api.TableException: Sink does not exists
FlinkSQL_1.12_用DDL实现Kafka到MySQL的数据传输_实现按照条件进行过滤写入MySQL_flink从kafka拉取数据并过滤数据写入mysql_旧城里的阳光的博客-CSDN博客 参考这篇文章,写了kafka到mysql的代码例子,因为自己改了表结构,运行下面代码&#x…...
【多线程】CAS 详解
CAS 详解 一. 什么是 CAS二. CAS 的应用1. 实现原子类2. 实现自旋锁 三. CAS 的 ABA 问题四. 相关面试题 一. 什么是 CAS CAS: 全称Compare and swap,字面意思:”比较并交换“一个 CAS 涉及到以下操作: 我们假设内存中的原数据 V,旧的预期值…...
卷积神经网络实现咖啡豆分类 - P7
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制🚀 文章来源:K同学的学习圈子 目录 环境步骤环境设置包引用全局设备对象 数据准备查看图像的信息制作数据集 模型设…...
C++之默认与自定义构造函数问题(二百一十七)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...
Docker从认识到实践再到底层原理(五)|Docker镜像
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…...
【Flowable】任务监听器(五)
前言 之前有需要使用到Flowable,鉴于网上的资料不是很多也不是很全也是捣鼓了半天,因此争取能在这里简单分享一下经验,帮助有需要的朋友,也非常欢迎大家指出不足的地方。 一、监听器 在Flowable中,我们可以使用监听…...
spring-kafka中ContainerProperties.AckMode详解
近期,我们线上遇到了一个性能问题,几乎快引起线上故障,后来仅仅是修改了一行代码,性能就提升了几十倍。一行代码几十倍,数据听起来很夸张,不过这是真实的数据,线上错误的配置的确有可能导致性能…...
【rpc】Dubbo和Zookeeper结合使用,它们的作用与联系(通俗易懂,一文理解)
目录 Dubbo是什么? 把系统模块变成分布式,有哪些好处,本来能在一台机子上运行,为什么还要远程调用 Zookeeper是什么? 它们进行配合使用时,之间的关系 服务注册 服务发现 动态地址管理 Dubbo是…...
ChatGPT的未来
随着人工智能的快速发展,ChatGPT作为一种自然语言生成模型,在各个领域都展现出了巨大的潜力。它不仅可以用于日常对话、创意助手和知识查询,还可以应用于教育、医疗、商业等各个领域,为人们带来更多便利和创新。 在教育领域&#…...
Pytorch模型转ONNX部署
开始以为会很困难,但是其实非常方便,下边分两步走:1. pytorch模型转onnx;2. 使用onnx进行inference 0. 准备工作 0.1 安装onnx 安装onnx和onnxruntime,onnx貌似是个环境。。倒是没有直接使用,onnxruntim…...
k8s优雅停服
在应用程序的整个生命周期中,正在运行的 pod 会由于多种原因而终止。在某些情况下,Kubernetes 会因用户输入(例如更新或删除 Deployment 时)而终止 pod。在其他情况下,Kubernetes 需要释放给定节点上的资源时会终止 po…...
面试题五:computed的使用
题记 大部分的工作中使用computed的频次很低的,所以今天拿出来一文对于computed进行详细的介绍,因为Vue的灵魂之一就是computed。 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护…...
完美的分布式监控系统 Prometheus与优雅的开源可视化平台 Grafana
1、之间的关系 prometheus与grafana之间是相辅相成的关系。简而言之Grafana作为可视化的平台,平台的数据从Prometheus中取到来进行仪表盘的展示。而Prometheus这源源不断的给Grafana提供数据的支持。 Prometheus是一个开源的系统监控和报警系统,能够监…...
黑马JVM总结(九)
(1)StringTable_调优1 我们知道StringTable底层是一个哈希表,哈希表的性能是跟它的大小相关的,如果哈希表这个桶的个数比较多,元素相对分散,哈希碰撞的几率就会减少,查找的速度较快,…...
如何使用 RunwayML 进行创意 AI 创作
标题:如何使用 RunwayML 进行创意 AI 创作 介绍 RunwayML 是一个基于浏览器的人工智能创作工具,可让用户使用各种 AI 功能来生成图像、视频、音乐、文字和其他创意内容。RunwayML 的功能包括: * 图像生成:使用生成式对抗网络 (…...
【css】能被4整除 css :class,判断一个数能否被另外一个数整除,余数
判断一个数能否被另外一个数整除 一个数能被4整除的表达式可以表示为:num%40,其中,num为待判断的数,% 为取模运算符,为等于运算符。这个表达式的意思是,如果num除以4的余数为0,则返回true&…...
ChatGPT与日本首相交流核废水事件-精准Prompt...
了解更多请点击:ChatGPT与日本首相交流核废水事件-精准Prompt...https://mp.weixin.qq.com/s?__bizMzg2NDY3NjY5NA&mid2247490070&idx1&snebdc608acd419bb3e71ca46acee04890&chksmce64e42ff9136d39743d16059e2c9509cc799a7b15e8f4d4f71caa25968554…...
关于 firefox 不能访问 http 的解决
情景: 我在虚拟机 192.168.x.111 上配置了 DNS 服务器,在 kali 上设置 192.168.x.111 为 DNS 服务器后,使用 firefox 地址栏搜索域名 www.xxx.com ,访问在 192.168.x.111 搭建的网站,本来经 192.168.x.111 DNS 服务器解…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
Python网页自动化Selenium中文文档
1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API,让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API,你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

