自己写了一个线程池,想把它和http服务连在一起,居然也不难。
http的内容在另一篇文章《简易http服务器》中贴出了,我把它做成了三个文件,httpd.c和httpd.h,只需调用httpsvr(client_sock)即可;我在《unix环境线程编程练习》里贴出了线程池和使用方法。
先把httpd的调用放到线程的“任务”函数里:
void work(void *arg)
{
int sock = (int)arg;
httpsvr(sock);
close(sock);
}
然后与《简易http服务器》中svrthread.c的类似,只是去掉 void* svrthread(void *p) ,在listen()调用之后,就创建线程池:
th_pool_str tpool;
if (init_tpool(&tpool, 8))
{
close(lsock);
_exit(1);
}
在循环内部,去掉创建线程的代码,替换成一句:
submit_task(&tpool, work, (void *)clientfd);
完整的代码是:
//svrtpool.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <netinet/in.h>
#include "httpd.h"
#include "threadpool.h"
//typedef struct sockaddr_in sockaddr_in;
//typedef struct sockaddr sockaddr;
#define PORT 80
#define BACKLOG 5
void work(void *arg)
{
int sock = (int)arg;
httpsvr(sock);
close(sock);
}
int main()
{
int lsock,clientfd;
lsock = socket(AF_INET,SOCK_STREAM,0);
if (-1 == lsock)
{
perror("socket error!\n");
_exit(1);
}
int sockopt_on = 1;
if (setsockopt(lsock,SOL_SOCKET,SO_REUSEADDR,&sockopt_on,sizeof(sockopt_on)))
{
perror("setsockopet error\n");
}
sockaddr_in host_addr;
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(PORT);
host_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (-1 == bind(lsock, (sockaddr *)&host_addr, sizeof(sockaddr_in)))
{
close(lsock);
perror("bind error!\n"); exit(1);
}
if (-1 == listen(lsock,BACKLOG))
{
close(lsock);
perror("listen error!\n");
_exit(1);
}
th_pool_str tpool;
if (init_tpool(&tpool, 8))
{
close(lsock);
_exit(1);
}
printf("server start on: %d\n",PORT);
sockaddr_in addr;
pthread_t svr_id;
int size_addr = sizeof(addr),ret;
for ( ; ; )
{
clientfd = accept(lsock,(sockaddr*)&addr,(socklen_t *)&size_addr);
if (-1 == clientfd)
{
perror("accept error!\n");
continue;
}
submit_task(&tpool, work, (void *)clientfd);
}
close_tpool(&tpool);
return 0;
}
系统创建和销毁线程的消耗比较费时,在访问量上升到一定数量是,效率就会明显下降;用线程池省去了这些消耗,随着访问数量增加效率,比之前的程序也愈来愈明显。
现在看来线程池早就不是新技术了,很多服务器内部其实都用了线程池,比如数据库系统和各种知名的web服务器。