I/O多路复用三种实现

news2024/11/14 15:27:24

 

一.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处理高并发,百万级

  1. 红黑树: 是特殊的二叉树(每个节点带有属性),Epoll怎样能监听很多个呢?首先创建树的根节点,每个节点都是一个fd以结构体的形式存储(节点里面包含了一些属性,callback函数)
  2. 就绪链表: 当某一个文件描述符产生事件后,会自动调用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;
}

对比 

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

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

相关文章

天翎知识管理系统:智能化搜索引擎,快速定位知识资源

关键词&#xff1a;知识管理系统、全文检索 编者按&#xff1a;在当今知识经济时代&#xff0c;企业所面临的知识资源越来越丰富&#xff0c;如何高效地管理和利用这些资源成为了一个重要的问题。天翎知识管理系统凭借其智能化搜索引擎&#xff0c;可以帮助企业快速定位知识资源…

论文管理系统设计与实现

毕业论文管理系统的设计与实现 学生&#xff1a; 指导教师&#xff1a; 内容摘要&#xff1a;毕业论文管理系统是典型的MIS信息管理系统,其开发主要包括后台数据库的建立和维护以及前端应用程序的开发两个方面。对于前者要求建立起数据一致性和完整性强、数据安全性好的库。而…

LeetCode【4. 寻找两个正序数组的中位数】

快乐安康 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 public double findMedianSortedArrays(int[] nums1, int[] nums2) {if (nums1.length &…

干净优雅的做iOS应用内全局交互屏蔽

本文字数&#xff1a;4930字 预计阅读时间&#xff1a;28分钟 01 交互屏蔽的需求 很多应用开发者都会遇到这样一个需求&#xff0c;当程序需要处理某个敏感的核心任务&#xff0c;或者执行某些动画时&#xff0c;需要杜绝一切外部干扰&#xff0c;优先保证任务的完成&#xff0…

EF Core 迁移失败、数据丢失 手动处理

一、环境 windows 10 Visual studio 2022 dotnet 6.0.404 Microsoft.EntityFrameworkCore.Tools 6.0.14 二、问题 有一记录房产交易数据的实体&#xff0c;已有生产数据&#xff0c;现需更改、添加字段&#xff0c;产生了迁移不成功和数据丢失的问题。 原实体定义 //唯一复合索…

数据结构与算法——11.递归

这篇文章我们来讲一个很常用的算法思想——递归 目录 1.递归的概述 2.用递归求阶乘 3.用递归反向打印字符串 4.用递归来求解二分查找 5.用递归解决冒泡排序 6.用递归解决插入排序 7.用递归解决斐波那契数列 8.用递归解决兔子问题 9.用递归解决青蛙爬楼梯问题 10.递归…

CSS - 鼠标移入整行高亮显示,适用于会员套餐各参数对比页面(display: table,div 转表格形式)

效果图 可根据基础示例和进阶示例&#xff0c;复制进行改造样式。 如下图所示&#xff0c;本文提供 2 个示例。 基础示例 找个 HTML 页面&#xff0c;一键复制运行。 <body><h1 style"text-align: center;">基础示例</h1><section class"…

软件设计模式系列之六——单例模式

1 模式的定义 单例模式&#xff08;Singleton Pattern&#xff09;是一种常见的创建型设计模式&#xff0c;其主要目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。这意味着无论何时何地&#xff0c;只要需要该类的实例&#xff0c;都会返回同一个…

JAVA高级技术入门(单元测试,反射,注解,动态代理)

JAVA高级技术入门&#xff08;单元测试&#xff0c;反射&#xff0c;注解&#xff0c;动态代理&#xff09; 一、Junit单元测试二、反射1.认识反射&#xff0c;获取类概念&#xff1a;快速入门&#xff1a;获取Class对象的三种方式 2.1获取类的构造器2.2获取类的构造器的作用&a…

计算机系统概述之计算机的发展历程

计算机系统概述之计算机的发展历程 计算机的发展历程计算机系统硬件的发展微处理器的发展 软件的发展CAD/ CAM/CIMS的简单介绍 思维导图总结 计算机的发展历程 计算机系统 计算机系统由硬件和软件组成。 硬件&#xff1a;指的是计算机实体&#xff0c;如&#xff1a;主机&#…

【深度学习】 Python 和 NumPy 系列教程(廿四):Matplotlib详解:2、3d绘图类型(10)3D箱线图(3D Box Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图&#xff08;3D Line Plot&#xff09; 2. 3D散点图&#xff08;3D Scatter Plot&#xff09; 3. 3D条形图&#xff08;3D Bar Plot&#xff09; 4. 3D曲面图…

动态规划——01背包

背包问题经典资料背包九讲&#xff0c;可以上网查一下相关资料。 下面的资料来自代码随想录和自己的一些个人理解&#xff0c;如有需要可以跳转代码随想录进行学习&#xff1a;代码随想录 (programmercarl.com) 背包一共分为01背包&#xff0c;完全背包&#xff0c;多重背包&am…

leetcode:70. 爬楼梯

一、题目 函数原型&#xff1a;int climbStairs(int n) 二、思路 此题运用递归思想。当只有1个台阶&#xff0c;那么只有1种方法爬到楼顶——跨一个台阶&#xff1b;当有2个台阶时&#xff0c;有2种方法爬到楼顶——跨一个台阶跨两次或直接跨两个台阶。当有3个台阶或更多台阶时…

实现客户端pineline的思路

背景&#xff1a; redis集群不支持客户端的mget操作&#xff0c;但是业务上对这个redis集群的批量操作的需求一直都在&#xff0c;所以有各种客户端实现了各式各样的pineline实现,本文就记录下我们公司的实现方式 pineline实现思路 1.pineline要快 pineline之所以快是因为可…

深度学习训练过程可视化工具

1.深度学习网络结构画图工具 地址&#xff1a;https://cbovar.github.io/ConvNetDraw/ 2.caffe可视化工具 输入&#xff1a;caffe配置文件 输出&#xff1a;网络结构 地址&#xff1a;http://ethereon.github.io/netscope/#/editor 3.深度学习可视化工具Visual DL Visual D…

数据变换:数据挖掘的准备工作之一

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

【毕设选题】flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

【python手写算法】numpy实现简易神经网络和反向传播算法【1】

import numpy as npdef dense(A,W):Znp.matmul(A,W)#矩阵乘法return 1/(1np.exp(-Z))if __name__ __main__:leanring_rate100Anp.array([[200.0,17.0]])# Wnp.array([[1,-3,5],# [-2,4,-6]])# bnp.array([[-1,1,2]])W1 np.array([[0., -10, 4],[-1,3,2]])W2np.ar…

数学建模__非线性规划Python实现

使用到的是scipy库 线性规划指的是目标模型均为线性&#xff0c;除此以外的都是非线性规划&#xff0c;使用scipy提供的方法对该类问题进行求解。 from scipy.optimize import minimize import numpy as np#定义目标函数 def fun(args):a,b,c,d argsv lambda x: (ax[0])/ (b…

免费和开源的机器翻译软件LibreTranslate

什么是 LibreTranslate &#xff1f; LibreTranslate 免费开源机器翻译 API&#xff0c;完全自托管。与其他 API 不同&#xff0c;它不依赖于 Google 或 Azure 等专有提供商来执行翻译。它的翻译引擎由开源 Argos Translate 库提供支持。 这个软件在 2022 年 3 月的时候折腾过&…