C++编程 (三)--- 深入C++后台开发_c++后端开发-程序员宅基地

    搞了很久搜索了,可是做的很多都是业务逻辑和PM的需求,也没有高大上的技术。感觉随着开源项目的成熟技术的门槛在逐渐的降低,曾经高大上的技术已经渐渐变得没什么门槛了。。。(记得脉脉上看到一句很好玩的话,程序员是一个高大上的职业,直到JAVA语言的出现。。。)不过我也还是认真总结和实践了一些深入的技术,在做业务的时候也有一些积累的吧。总的来说C++后台开发深入一些的有网络编程、多线程编程、进程/线程同步/通信和调度、动态链接库使用、常用的框架的深入阅读和理解、常用的运行时程序问题排查(内存泄露、无法响应新的请求)、分布式系统的使用、高并发系统优化。所以本文一共分为如下十一个部分:

一、网络编程

二、多线程编程

三、多进程/线程同步/通信/调度

四、动态链接库使用

五、开源框架的深入阅读和理解(以thrift为例)

六、常用运行时程序问题排查

七、分布式系统问题

八、高并发系统的优化

九、代码风格和设计模式

十、C++语言的新特性

十一、Linux内核知识


一、网络编程:

             1. TCP与UDP:
                      a. TCP与UDP报头
                          1). TCP报头(最少20个字节):源端口、目的端口、序号、确认号、数据偏移、ACK、RST、SYN、FIN、窗口、校检和、紧急指针、选项、填充。
                          2). UDP报头:(8个字节)预案端口、目的端口、用户数据包长度、校检和、数据。
                      b. TCP与UDP的区别
                          1). TCP是面向连接的服务,有拥塞控制和超时重传,因此有滑动窗口。
                          2). UDP是非面向连接的服务,支持一对多通信,如广播。

             2. TCP 3次握手、4次挥手过程:

                               
                            a. TCP三次握手、四次挥手漏洞分析,天网如何使用TCP协议中断翻墙协议访问。
                                    为什么要进行三次握手?因为双方要交换序列号和窗口大小,发送方确认接收方接收到了syn请求,防止出现死锁。
                                   TCP三次握手的漏洞,SYN FLOOD攻击:客户端不停的伪造IP来给服务端发起请求,服务端对每个syn都要分配一个TCB,通常每个TCB至少280个字节。应对:syn cookie技术,使用双方通信信息、MSS、时间等计算,看看与对方回报文中的sequence number是否相同。
                                   TCP四次挥手?因为TCP连接有个半连接状态,假设AB要释放,那么A发送了一个释放请求给B,B立即回复确认。但在此之间B发送的数据A依然需要接受,B需要回复给A它不再发送数据了。
                                   为什么TIME_WAIT需要有2MSL的时间?为了避免最后一个ACK没有被接收到,预留重发时间。

                  3.  多路IO复用模型:
                          a. 阻塞、非阻塞:
应用程序的调用是否立即返回!
                          b. 异步、同步:
数据拷贝的时候进程是否阻塞!
                          c. select、poll、epoll
                              1). 三种IO复用模型对比:
                                        select支持最大打开文件数目有限(一般select使用32个32位整数作为文件描述符集)、用户态数据需要拷贝到内核态、每次都需要线性遍历每个FD,速度太慢; poll最大打开fd数目不限;epoll克服了上面所有的缺点,但是如果每个连接都是活跃的,效率也不高。
                              2). select:
                                        select执行流程:
                                        a). 
设置maxfd,将fd加入select监控集,使用一个array保存放到select监控集中的d,一是用于在select返回后,array作为源数据和fdset进行fd_isset判断。二是在select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select都要从array取得fd逐一加入。(select模型必须在select前循环array(加fd,取maxfd),返回后循环array。

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
参数:    

        sockfd: socket 函数返回的套接字描述符
        readfds : 读描述符集合
        writefds: 写描述符集合
        errorfds: 错误描述符集合
        timeout:  超时
返回值
        成功:返回值 0:无 >0:描述符就绪的总位数
        错误:返回INVALID_SOCKET(-1)

示例:

<span style="font-size:12px;">/* 实现功能:通过select处理多个socket
 * 监听一个端口,监听到有链接时,添加到select的w.
 */
#include "select.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>

typedef struct _CLIENT{
    int fd;
    struct sockaddr_in addr; /* client's address information */
} CLIENT;

#define MYPORT 59000

//最多处理的connect
#define BACKLOG 5
//最多处理的connect
CLIENT client[BACKLOG];

//当前的连接数
int currentClient = 0; 

//数据接受 buf
#define REVLEN 10
char recvBuf[REVLEN];
//显示当前的connection
void showClient();

int main()
{
    int i, ret, sinSize;
    int recvLen = 0;
    fd_set readfds, writefds;
    int sockListen, sockSvr, sockMax;
    struct timeval timeout;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;

    for(i=0; i<BACKLOG; i++)
    {
        client[i].fd = -1;
    }

    //socket
    if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket error\n");
        return -1;
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family  =  AF_INET;
    server_addr.sin_port = htons(MYPORT);
    server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY); 

    //bind
    if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
    {
        printf("bind error\n");
        return -1;
    }

    //listen
    if(listen(sockListen, 5) < 0)
    {
        printf("listen error\n");
        return -1;
    }

    for(i=0; i<BACKLOG; i++)
    {
        client[i].fd = -1;
    }

    //select
    while(1)
    {
        FD_ZERO(&readfds);
        FD_SET(sockListen, &readfds);
        sockMax = sockListen;
    
        //加入client
        for(i=0; i<BACKLOG; i++)
        {
            if(client[i].fd >0)
            {
                FD_SET(client[i].fd, &readfds);
                if(sockMax<client[i].fd) 
                    sockMax = client[i].fd;
            }
        }
        
        timeout.tv_sec=3;                
        timeout.tv_usec=0;
        //select
        ret = select((int)sockMax+1, &readfds, NULL, NULL, &timeout);
        if(ret < 0)
        {
            printf("select error\n");
            break;
        }
        else if(ret == 0)
        {
            printf("timeout ...\n");
            continue;
        }
        printf("test111\n");
    
        //读取数据
        for(i=0; i<BACKLOG; i++)
        {
            if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds))
            {
                if(recvLen != REVLEN)
                {
                    while(1)
                    {
                        //recv数据
                        ret = recv(client[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);
                        if(ret == 0)
                        {
                            client[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                        else if(ret < 0)
                        {
                            client[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                        //数据接受正常
                        recvLen = recvLen+ret;
                        if(recvLen<REVLEN)
                        {
                            continue;
                        }
                        else
                        {
                            //数据接受完毕
                            printf("%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf);
                            //close(client[i].fd);
                            //client[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                    }
                }
            }
        }
    
        //如果可读
        if(FD_ISSET(sockListen, &readfds))
        {
            printf("isset\n");
            sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr
        
            if(sockSvr == -1)
            {
                printf("accpet error\n");
            }
            else
            {
                currentClient++;
            }
        
            for(i=0; i<BACKLOG; i++)
            {
                if(client[i].fd < 0)
                {
                    client[i].fd = sockSvr;
                    client[i].addr = client_addr;
                    printf("You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) );
                    break;
                }
            }
            //close(sockListen);
        }
    }

    printf("test\n");
    return 0;
}

//显示当前的connection
void showClient()
{
    int i;
    printf("client count = %d\n", currentClient);

    for(i=0; i<BACKLOG; i++)
    {
        printf("[%d] = %d", i, client[i].fd);
    }
    printf("\n");
}</span>


                                        b).  int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:
    fds
:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;

    nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;

    timeout:是poll函数调用阻塞的时间,单位:毫秒;

返回值:
    >0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;

    ==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的       socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;

     -1: poll函数调用失败,同时会自动设置全局变量errno;
示例:

<span style="font-size:12px;">/* 实现功能:通过poll, 处理多个socket
 * 监听一个端口,监听到有链接时,添加到poll.
 */
#include "select.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/time.h>
#include <netinet/in.h>

typedef struct _CLIENT{
    int fd;
    struct sockaddr_in addr; /* client's address information */
} CLIENT;

#define MYPORT 59000

//最多处理的connect
#define BACKLOG 5

//当前的连接数
int currentClient = 0; 

//数据接受 buf
#define REVLEN 10
char recvBuf[REVLEN];

#define OPEN_MAX 1024

int main()
{
    int i, ret, sinSize;
    int recvLen = 0;
    fd_set readfds, writefds;
    int sockListen, sockSvr, sockMax;
    int timeout;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;

    struct pollfd clientfd[OPEN_MAX];


    //socket
    if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket error\n");
        return -1;
    }

    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family  =  AF_INET;
    server_addr.sin_port = htons(MYPORT);
    server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY); 

    //bind
    if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
    {
        printf("bind error\n");
        return -1;
    }

    //listen
    if(listen(sockListen, 5) < 0)
    {
        printf("listen error\n");
        return -1;
    }


    //clientfd 初始化
    clientfd[0].fd = sockListen;
    clientfd[0].events = POLLIN; //POLLRDNORM;
    sockMax = 0;
    for(i=1; i<OPEN_MAX; i++)
    {
        clientfd[i].fd = -1;
    }

    //select
    while(1)
    {
        timeout=3000;                
        //select
        ret = poll(clientfd, sockMax+1, timeout);
    
        if(ret < 0)
        {
            printf("select error\n");
            break;
        }
        else if(ret == 0)
        {
            printf("timeout ...\n");
            continue;
        }
    
        if (clientfd[0].revents & POLLIN)//POLLRDNORM
        {
            sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr
        
            if(sockSvr == -1)
            {
                printf("accpet error\n");
            }
            else
            {
                currentClient++;
            }
        
            for(i=0; i<OPEN_MAX; i++)
            {
                if(clientfd[i].fd<0)
                {
                    clientfd[i].fd = sockSvr;
                    break;
                }
            }
            if(i==OPEN_MAX)
            {
                printf("too many connects\n");
                return -1;
            }
            clientfd[i].events = POLLIN;//POLLRDNORM;
            if(i>sockMax)
                sockMax = i;
        }
    
        //读取数据
        for(i=1; i<=sockMax; i++)
        {
            if(clientfd[i].fd < 0)
                continue;
        
            if (clientfd[i].revents & (POLLIN | POLLERR))//POLLRDNORM
            {
                if(recvLen != REVLEN)
                {
                    while(1)
                    {
                        //recv数据
                        ret = recv(clientfd[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);
                        if(ret == 0)
                        {
                            clientfd[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                        else if(ret < 0)
                        {
                            clientfd[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                        //数据接受正常
                        recvLen = recvLen+ret;
                        if(recvLen<REVLEN)
                        {
                            continue;
                        }
                        else
                        {
                            //数据接受完毕
                            printf("buf = %s\n",  recvBuf);
                            //close(client[i].fd);
                            //client[i].fd = -1;
                            recvLen = 0;
                            break;
                        }
                    }
                }
            }
        }
    }

    return 0;
}</span>

                                        c).  int epoll_create(int size);

                                              int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );

                                              int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);

epoll_create生成一个 Epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的 socket fd 上是否发生以及发生了什么事件。 size 就是你在这个 Epoll fd 上能关注的最大 socket fd 数,大小自定,只要内存足够。
epoll_ctl控制某个 Epoll 文件描述符上的事件:注册、修改、删除。其中参数 epfd 是 epoll_create() 创建 Epoll 专用的文件描述符。相对于 select 模型中的 FD_SET 和 FD_CLR 宏。op:EPOLL_CTL_ADD  Register the target file descriptor fd on the epoll instance, EPOLL_CTL_MOD Change the event event associated with the target file descriptor fd, EPOLL_CTL_DEL Remove  (deregister)  the  target  file descriptor fd from the epoll instance。

<span style="font-size:12px;">/* 实现功能:通过epoll, 处理多个socket
 * 监听一个端口,监听到有链接时,添加到epoll_event
 */
#include "select.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <netinet/in.h>
typedef struct _CLIENT{
    int fd;
    struct sockaddr_in addr; /* client's address information */
} CLIENT;
#define MYPORT 59000
//最多处理的connect
#define MAX_EVENTS 500
//当前的连接数
int currentClient = 0; 
//数据接受 buf
#define REVLEN 10
char recvBuf[REVLEN];
//EPOLL相关 
//epoll描述符
int epollfd;
//事件数组
struct epoll_event eventList[MAX_EVENTS];
void AcceptConn(int srvfd);
void RecvData(int fd);

int main()
{
    int i, ret, sinSize;
    int recvLen = 0;
    fd_set readfds, writefds;
    int sockListen, sockSvr, sockMax;
    int timeout;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    //socket
    if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket error\n");
        return -1;
    }
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family  =  AF_INET;
    server_addr.sin_port = htons(MYPORT);
    server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY); 
    //bind
    if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
    {
        printf("bind error\n");
        return -1;
    }
    //listen
    if(listen(sockListen, 5) < 0)
    {
        printf("listen error\n");
        return -1;
    }
    //1. epoll 初始化
    epollfd = epoll_create(MAX_EVENTS);
    struct epoll_event event;
    event.events = EPOLLIN|EPOLLET;
    event.data.fd = sockListen;
    //2. epoll_ctrl
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sockListen, &event) < 0)
    {
        printf("epoll add fail : fd = %d\n", sockListen);
        return -1;
    }
    //epoll
    while(1)
    {
        timeout=3000;                
        //3. epoll_wait
        int ret = epoll_wait(epollfd, eventList, MAX_EVENTS, timeout);
        if(ret < 0)
        {
            printf("epoll error\n");
            break;
        }
        else if(ret == 0)
        {
            printf("timeout ...\n");
            continue;
        }
        //直接获取了事件数量,给出了活动的流,这里是和poll区别的关键
        int n = 0;
        for(n=0; n<ret; n++)
        {
            //错误退出
            if ((eventList[n].events & EPOLLERR) ||
                (eventList[n].events & EPOLLHUP) ||
                !(eventList[n].events & EPOLLIN))
            {
              printf ( "epoll error\n");
              close (eventList[n].data.fd);
              return -1;
            }
            
            if (eventList[n].data.fd == sockListen)
            {
                AcceptConn(sockListen);
        
            }else{
                RecvData(eventList[n].data.fd);
                //不删除
             //   epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent);
            }
        }
    }
    close(epollfd);
    close(sockListen);
    printf("test\n");
    return 0;
}
/**************************************************
函数名:AcceptConn
功能:接受客户端的链接
参数:srvfd:监听SOCKET
***************************************************/
void AcceptConn(int srvfd)
{
    struct sockaddr_in sin;
    socklen_t len = sizeof(struct sockaddr_in);
    bzero(&sin, len);
    int confd = accept(srvfd, (struct sockaddr*)&sin, &len);
    if (confd < 0)
    {
       printf("bad accept\n");
       return;
    }else
    {
        printf("Accept Connection: %d", confd);
    }
    //setnonblocking(confd);
    //4. epoll_wait
    //将新建立的连接添加到EPOLL的监听中
    struct epoll_event event;
    event.data.fd = confd;
    event.events =  EPOLLIN|EPOLLET;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, confd, &event);
}

//读取数据
void RecvData(int fd)
{
    int ret;
    int recvLen = 0;
    
    memset(recvBuf, 0, REVLEN);
    printf("RecvData function\n");
    
    if(recvLen != REVLEN)
    {
        while(1)
        {
            //recv数据
            ret = recv(fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);
            if(ret == 0)
            {
                recvLen = 0;
                break;
            }
            else if(ret < 0)
            {
                recvLen = 0;
                break;
            }
            //数据接受正常
            recvLen = recvLen+ret;
            if(recvLen<REVLEN)
            {
                continue;
            }
            else
            {
                //数据接受完毕
                printf("buf = %s\n",  recvBuf);
                recvLen = 0;
                break;
            }
        }
    }

    printf("content is %s", recvBuf);
}</span>

                   4. 网络协议分层模型: 
                   5. 其他一些小问题:

                       a. 网络模型直接排序一般采用大端存储的。
                       b. keepalive???:
                       c. 长连接、短连接:
                       d. 防火墙如何利用TCP协议终止你的翻墙请求?

                   6. 网络分层模型和不同层的支持的协议:

(如何画时序图)

二、多线程编程:

                   1. 线程与进程:
                        线程共享:进程指令、大多数数据、打开的文件、信号处理函数和信号处置、当前工作目录、用户id和组ID
                        进程共享:线程ID、寄存器集合(程序计数器和函数指针)、栈、errno、信号掩码、优先级
                        线程&进程:线程是系统调度的最小单元,fork需要把父进程内存影响复制到子进程;fork返回以后父子进程需要进行IPC通信。 

                   2. 多线程编程:
                        pthread_create( pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void arg);
                                    pthread_join(pthread_t *tid, void **status) //如果status指针非空,把线程的返回值、一个指向某个对象的指针存入status指向的位置
                                    pthread_self(void) // 线程自身id
                                    pthread_detach(tid)// 线程终止时,所有资源被释放
                                    pthread_exit(void *status)//线程终止

                   3. 多进程编程:
                        fork()//创建子进程
                                    exec()//4个函数族。创建进程,但子进程执行时废弃当前进程的数据段和堆栈段

                   4. 进程/线程间同步\通信:

                      一般我们讨论进程间共享数据,线程间同步。因为线程很多数据都是共有的,而进程间很难共享数据,因此线程的同步和进程的数据共享一般都是我们面临的问题的。
                      a. 进程间共享/通信数据(IPC)方法:
                          管道、FIFO、共享内存、消息队列、信号、socket
                           对比:
                                    1. 管道:速度慢、容量有限、只有父子进程通讯
                                    2. FIFO: 任何进程都能通信,但速度慢
                                    3. 消息队列:容量受限
                                    4. 信号量:不能传递复杂信息
                                    5. 共享内存:速度快、容量大。需要注意同步


                          1). 
                      b. 线程间同步方法(SYNC):
                          临界区、锁、信号量、事件、interlocked variable

                          1). 锁:

                                            pthread_mutex_t counter_mutex=PTHREAD_MUTEX_INITIALIZER; COND

                                            pthread_mutex_lock(pthread_mutex_t * mptr);
                                            // do something

                                            pthread_mutex_unlock(pthread_mutex_t *mptr);


                           http://www.cnblogs.com/memewry/archive/2012/08/22/2651696.html

五、开源框架深入阅读和理解:

                 1. thrift协议的数据类型、协议、传输、服务类型
                 2. thrift协议源代码阅读

六、常用运行时程序排查:

                 1. 使用cp替换so文件为什么服务会core?
                 2. 如何排查内存泄露

七、分布式系统问题:

十、C++语言的新特性:

             1. 智能指针 std::shared_ptr 用法:
                           a). 智能指针是用来实现指针对象的共享和内存生存期自动管理(一般使用引用计数实现,我理解智能指针就是一个栈对象,在智能指针的生命期结束时,对智能指针指向的动态内存使用减1,如果计数减为0,则释放内存)。
                           b). 所有的智能指针都会重载* -> 等符号。

#include <boost/shared_ptr.hpp>
using namespace std;
class implementation
{
public:
    ~implementation() { std::cout <<"destroying implementation\n"; }
    void do_something() { std::cout << "did something\n"; }
};
void test(){ 
    boost::shared_ptr<implementation> sp1(new implementation()); 
    boost::shared_ptr<implementation> sp2 = sp1; 
    sp2.reset(); 
    boost::shared_ptr<int> a1(ptr); 
    std::cout<<*ptr<<endl;
}

                           b). 使用智能指针注意事项:
                                 a. 不要把原生指针给多个shared_ptr管理。
                                 b. 不要在函数实参里创建shared_ptr。
                                 c. shared_ptr作为被保护对象时小心循环引用。

                                 d. 不要把this指针传给shared_ptr。

                           c). static_ptr_cast ???:
                                 

                  2. auto用法:

参考文献:
1. Unix网络编程:
2. thrift服务源代码分析: http://yanyiwu.com/work/2014/12/06/thrift-tnonblockingserver-analysis.html
3. 


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/china1000/article/details/49472661

智能推荐

MyBatis Plus (三) --------- 入门 HelloWorld_mybatis-plus helloword-程序员宅基地

文章浏览阅读238次。MyBatis Plus 使用入门_mybatis-plus helloword

BERT模型对抗性攻击分析研究:了解对抗样本及防御方法_bert对抗攻击-程序员宅基地

文章浏览阅读560次,点赞4次,收藏8次。BERT模型基本原理简介BERT(Bidirectional Encoder Representations from Transformers)是一种基于Transformer架构的预训练语言模型,其核心思想是通过双向上下文来学习文本表示。双向注意力机制:BERT模型使用Transformer中的注意力机制来实现双向编码,能够充分考虑位置前后的上下文信息。多层Transformer编码器。_bert对抗攻击

30岁转行程序员晚了吗?分享30岁转行的经历_30岁转行是不是太晚了-程序员宅基地

文章浏览阅读436次。我是85年的,计算机专业普通本科毕业。在一个二线城市,毕业后因为自身能力问题、认知水平问题,再加上运气不好,换过多份工作,每份工作都干不长。导致我30多岁时,还一事无成,也几乎没有积累到什么经验技术、行业知识等。甚至还一度去开过网约车,送过外卖。转行做程序员前,我每个月收入仅三四千元。2017年下定决心,准备转行!我知道自己最大的劣势就是年龄太大了,但考虑再三,因为以下几个原因,我还是决定走这条路:我目前的情况已经算是城市里的底层了。我不会做生意,没其它经验和技能,性格偏内向,销售和交际也不太擅长。_30岁转行是不是太晚了

第三章:PyTorch的主要组成模块_complexpytorch-程序员宅基地

文章浏览阅读1.4k次,点赞3次,收藏16次。一般来说,input为sigmoid激活层的输出,或者softmax的输出。PyTorch数据读入是通过Dataset+DataLoader的方式完成的,Dataset定义好数据的格式和数据变换形式,DataLoader用iterative的方式不断读入批次数据。某些任务在训练完成后,需要对一些必要的内容进行可视化,比如分类的ROC曲线,卷积网络中的卷积核,以及训练/验证过程的损失函数曲线等等。损失函数是数据输入到模型当中,产生的结果与真实标签的评价指标,我们的模型可以按照损失函数的目标来做出改进。_complexpytorch

【每日一题】Day0012:力扣题库NO.1044. 最长重复子串_vwnexev-程序员宅基地

文章浏览阅读544次。今天的题虽然通过自己的思路应该是能够解出答案,但是提交超时;去学习别人的思路和解法,因为没有算法基础也难懂,不过姑且先将自己的思路述出。题目链接:力扣1044最长重复子串https://leetcode-cn.com/problems/longest-duplicate-substring/思路很简单,都在注释里直接看代码:package cn.daycode.leetcode;public class LongestDupSubstring { public static voi_vwnexev

103-MHA 高可用mysql集群-程序员宅基地

文章浏览阅读83次。一.MHA简介1.1.简介 MHA(Master HA ) 是一款开源的 MySQL高可用程序,它为MYSQL 主从复制架构提供了automating master failover主节点自动迁移功能.MHA在监控到master节点故障时候,会自动提升拥有的数据最近进于主节点的其他从节点为主节点,并且在此期间,会通过其他节点获取额外的信息来避免数据不一致性的问题..._高可用报错,fri may 22 20:13:46 2020 - [warning] sql thread is stopped(no err

随便推点

多元预测|开普勒算法优化注意力机制卷积神经网络结合双向门控循环单元KOA-Attention-CNN-biGRU实现光伏预测附matlab代码-程序员宅基地

文章浏览阅读1.4k次,点赞9次,收藏10次。光伏预测是光伏发电系统稳定运行和电网安全的重要保障。本文提出了一种基于开普勒算法优化注意力机制卷积神经网络结合双向门控循环单元(KOA-Attention-CNN-biGRU)的光伏预测模型。该模型通过开普勒算法优化注意力机制,增强了模型对光伏时间序列数据的局部特征提取能力;同时,结合双向门控循环单元,充分利用了光伏时间序列数据的长期依赖关系,提高了预测精度。在北神山光伏电站实际数据上的实验结果表明,所提出的模型在预测精度和鲁棒性方面均优于传统的光伏预测模型。引言。

2016,自律又惊喜的一年-程序员宅基地

文章浏览阅读143次。2016年度目标完成情况跑步,完成一次半程马拉松(已完成)练成六块腹肌(未完成,只有一块腹肌,应该是七分吃,三分练)趁早效率手册、三只青蛙、番茄工作法、践行GTD、100小时定律(践行效率不高,但基本都在做)敢于表达自我,专注于演讲技能 ,10次演讲 (8/10)英语能力提升,练习口语和阅读(上半年是以英语单词打卡为主,下半年是以英语每日一句,英语麦克风打卡,听的用开言英语,阅读是...

Pandas基础介绍-程序员宅基地

文章浏览阅读945次,点赞19次,收藏20次。Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入数据,并对数据进行高效的操作和分析。Pandas官网地址Pandas文档。_pandas

mac webrtc android,WebRTC 开发(五)编译与运行 Mac 工程-程序员宅基地

文章浏览阅读151次。Last login: Fri Oct 18 20:59:20 on ttys000The default interactive shell is now zsh.To update your account to use zsh, please run `chsh -s /bin/zsh`.For more details, please visit https://support.apple..._ardexternalsamplecapturer

::在C++中的意思_c++ ::代表-程序员宅基地

文章浏览阅读934次。::表示作用域,和所属关系。class Aint A::test() //表示test是属于A类的。关于::的具体解析:::是运算符中等级最高的,它分为三种:1)global scope(全局作用域符),用法(::name)。2)class scope(类作用域符),用法(class::name)。3)namespace scope(命名空间作用域符),用法(namespace::n..._c++ ::代表

如何在Arcgis中对图斑进行自上而下,从左往右地编号_arcgis编号号从上到下,从左到右-程序员宅基地

文章浏览阅读3w次,点赞20次,收藏90次。在实际项目中,需要我们按照自上而下,从左往右的顺序为图斑编号,并且多数时候序号位数是确定的,针对这个问题我总结了一个自认为还算简便的方法。下面是具体的方法步骤:1、计算Xmin与Ymax。利用坐标进行排序,首先要算出坐标值。需要说明的是这里没有直接利用质心坐标而采用Xmin、Ymax进行排序,是因为质心坐标会遇到一种情况,就是当这个图斑很长或者很宽时,本应排在前面的序号,而因为质心靠后不得不被..._arcgis编号号从上到下,从左到右

推荐文章

热门文章

相关标签