线程池的C++实现

该博客详细介绍了如何使用线程池和epoll实现一个TCP文件传输系统。首先初始化线程池,接着建立TCP监听套接字,利用epoll进行多路复用。当有客户端连接时,将任务放入线程池的任务队列,由工作线程处理文件传输。整个过程涉及到了线程同步、网络编程和文件操作等关键步骤。
摘要由CSDN通过智能技术生成
//先看主体流程,再细分到具体函数的实现。
//首先初始化线程池,里面涉及到初始化任务队列,然后开启线程池,即循环创建线程
//准备工作做完后,就可以处理客户端的请求了。建立TCP监听套接字,并且初始化一下
//即bind绑定ip,port
//再用epoll去管理套接字,进入while(1)循环,处理具体的客户端请求
//以下是以传输文件为任务实现的工作线程。
int main(int argc,char* argv[]){
    ARGS_CHECK(argc,4);

    int threadNum = atoi(argv[3]);
    //创建线程池结构体并初始化
    threadpool_t threadpool;
    threadPoolInit(&threadpool,threadNum);
    //启动线程池
    threadPoolStart(&threadpool); 

    //建立TCP连接
    int listenFd = 0;
    tcpInit(&listenFd,argv[1],argv[2]);

    //epoll监听套接字
    int epollfd = epoll_create(1);
    ERROR_CHECK(epollfd,-1,"epoll_create");

    //添加文件描述符
    epollAdd(epollfd,listenFd);
    
    int readyNum = 0;
    struct epoll_event evs[2];
    memset(&evs,0,sizeof(evs));
    int clientFd = 0;
    while(1){
        readyNum = epoll_wait(epollfd,evs,2,-1);
        for(int index = 0;index < readyNum;++index){
            if(evs[index].data.fd == listenFd){
                clientFd = accept(listenFd,NULL,NULL);
                if(clientFd < 0){
                    perror("accept");
                    return -1;
                }

                pNode_t pNew = (pNode_t)calloc(1,sizeof(node_t));
                pNew->client = clientFd;
                pthread_mutex_lock(&threadpool.que.mutex);
                queInsert(&threadpool.que,pNew);
                pthread_mutex_unlock(&threadpool.que.mutex);
                pthread_cond_signal(&threadpool.que.cond);

            }
        }

    }
    close(listenFd);
    return 0;
}

第一步:定义相关结构体

  • 线程池结构体:线程数量,任务队列,线程池开启标志位,保存线程ID的数组
  • 任务队列结构体:头尾指针,队列大小,互斥锁,信号量
  • 任务节点:记录客户的套接字的描述符,指向下一个节点的指针。
//任务节点
typedef struct Node{
    int client;//记录客户的套接字描述符
    struct Node* pNext;//指向下一个节点的指针
}node_t,*pNode_t;

typedef struct{
    pNode_t pHead,pTail;//队列头尾指针 
    int size;//队列大小
    pthread_mutex_t mutex; //互斥锁
    pthread_cond_t cond;//信号
}que_t,*pQue_t;

typedef struct{
    que_t que;//任务队列
    int threadNums;//线程数量
    bool isStar;//线程池开启标志位
    pthread_t* pThid;//保存线程ID的数组
    
}threadpool_t,*pThreadpool_t;

第二步:线程池的初始化和开启

//线程池初始化,开启
void threadPoolInit(pThreadpool_t pthreadpool,int threadNum){
    memset(pthreadpool,0,sizeof(threadpool_t));
    queInit(&pthreadpool->que);//初始化任务队列
    pthreadpool->threadNums = threadNum;
    pthreadpool->isStar = false;
    //申请线程ID的数组空间
    pthreadpool->pThid =(pthread_t*)calloc(threadNum,sizeof(pthread_t));
}

void threadPoolStart(pThreadpool_t pthreadpool){
    if(pthreadpool->isStar == false){
        for(int index = 0;index < pthreadpool->threadNums;++index){
            pthread_create(&pthreadpool->pThid[index],NULL,threadFunc,pthreadpool);
        }
    }
    pthreadpool->isStar = true;
}

第三步:初始化任务队列,和任务队列的相关操作

//队列的一些操作实现
void queInit(pQue_t pQue){
    memset(&pQue,0,sizeof(que_t));
    pthread_mutex_init(&pQue->mutex,NULL);
    pthread_cond_init(&pQue->cond,NULL);
    pQue->size = 0;
    pQue->pHead = NULL;
    pQue->pTail = NULL;
}
//插入任务节点
void queInsert(pQue_t pQue,pNode_t pNew){
    if(pQue->pHead == NULL){
        pQue->pHead = pNew;
        pQue->pTail = pNew;
    }else{
        pQue->pTail->pNext = pNew;
        pQue->pTail = pNew;
    }
    ++pQue->size;
}
//获取任务节点
int queGet(pQue_t pQue,pNode_t* pGet){
    if(0 == pQue->size){
        return -1;
    }
    *pGet = pQue->pHead;
    pQue->pHead = pQue->pHead->pNext;
    if(NULL == pQue->pHead){
        pQue->pTail = NULL;
    }
    pQue->size--;
    return 1;
}

第四步:建立tcp连接

//初始化套接字,绑定端口号,IP地址
int tcpInit(int* socketFd,char* ip,char* port){
    int  listenFd = socket(AF_INET,SOCK_STREAM,0);
    if(listenFd < 0){
        perror("socket");
    }
    //创建监听套接字
    int reuse = 1;
    int ret = setsockopt(listenFd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
    if(ret < 0){
        perror("setsockopt");
    }
    
    struct sockaddr_in serAddr;
    memset(&serAddr,0,sizeof(serAddr));
    serAddr.sin_family = AF_INET;
    serAddr.sin_addr.s_addr = inet_addr(ip);
    serAddr.sin_port = htons(atoi(port));

    //绑定IP,port
    ret = bind(listenFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
    if(ret< 0){
        perror("setsockopt");
    }
    ret = listen(listenFd,10);

    *socketFd = listenFd;

    return *socketFd;
}

第五步:epoll监听套接字

//epoll多路复用,相关操作
int epollAdd(int epollfd,int listenfd){
    struct epoll_event event;
    memset(&event,0,sizeof(event));

    event.data.fd = listenfd;
    event.events = EPOLLIN;

    int ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
    if(ret< 0){
        perror("epoll_ctl");
    }   
    return 0;
}

第六步:定义工作函数,处理客户端的具体工作

//工作线程,主要负责与客户端进行交互的具体动作
void* threadFunc(void* pArg){
    pThreadpool_t pPool = (pThreadpool_t)pArg;
    int getSucess = 0;
    pNode_t pCur = NULL;
    while(1){
        //加锁,取出任务,如果队列中没任务就等待被唤醒
        pthread_mutex_lock(&pPool->que.mutex);
        if(0 == pPool->que.size){
            printf("child wait\n");
            pthread_cond_wait(&pPool->que.cond,&pPool->que.mutex);
            printf("child wake up\n");
        }
        getSucess = queGet(&pPool->que,&pCur);
        pthread_mutex_unlock(&pPool->que.mutex);
        //取出任务后,进行文件传输
        if(getSucess != -1){
            transfile(pCur->client);
            close(pCur->client);
        }
    }
}

//传输线程
void transfile(int clientfd){
    int fd = open(FILENAME,O_RDWR);
    if(fd == -1){
        perror("open FILENAME is false");
    }
    int ret = 0;
    train_t train;
    int headLen = sizeof(train.dataLen);
    memset(&train.buf,0,sizeof(train.buf));
    strcpy(train.buf,FILENAME);
    train.dataLen = sizeof(FILENAME);
    
    ret = send(clientfd,&train,headLen+train.dataLen,0);
    if(ret < 0) {
        perror("headLen send is false!");
    }
    //发送文件大小
    struct stat filestat;
    memset(&filestat,0,sizeof(filestat));
    ret = fstat(fd,&filestat);
    if(ret == -1){
        perror("fstat is false!");
    }
    memcpy(train.buf,&filestat.st_size,sizeof(filestat.st_size));

    train.dataLen = sizeof(filestat.st_size);
    ret = send(clientfd,&train,headLen+train.dataLen,0);


    //读取内容并发送
    char* pMap = (char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(pMap == (char*)-1){
        perror("mmap is false");
    }

    ret = send(clientfd,pMap,filestat.st_size,0);

    munmap(pMap,filestat.st_size);

    close(fd);

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值