线程池的引入
多进程多线程创建的时机是事件就绪后,为处理这个就绪事件创建子进程或者函数线程。
结束的时机是将事件处理完成之后结束相应的子进程或者函数线程。
这个时候服务器就会有很严重的效率问题,当服务器运行起来之后频繁得创建和结束对系统的负担是很重的。问题如下:
- 是需要消耗时间和系统资源的。
- 而且当系统运行起来之后就会有不可预知的崩溃。
- 对于客户端的响应速度相对较慢。
- 事件就绪和处理事件之间存在创建进程或者线程的过程。
- 如果服务器的系统上存在大量的进程或者线程,就需要不断的进行进程或者线程的切换,从而致使处理业务的时间变少。
核心的问题就在于在运行这个程序之前我们是不知道会创建多少个进程或者是线程。为了解决这个问题我们就引入了线程池和进程池的概念。
线程池的概念
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
在服务程序启动时,程序会创建固定数量(和CPU的核数有关)的线程。这些线程的执行逻辑相同。将创建的线程维护在逻辑的池中(将所有创建的线程的执行阻塞),当主线程检测到有事件就绪,将就绪的事件通过某种方式传递给线程池中的一个线程(将一个线程唤醒)。
当服务器程序终止时,结束所有的线程。
线程池的工作机制
线程池的工作机制
- 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
- 一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
线程池的应用
线程池主要用于
- 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。 因为单个任务小,而任务数量巨大,一个热门网站的点击次数会很多。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
- 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
- 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
利用线程池实现TCP服务器程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/epoll.h>
#include<semaphore.h>
#define EVENTSIZE 128
#define PTHREADNUM 5//线程池中线程的数量
pthread_t id[PTHREADNUM]
sem_t sem;
int Buff[ARRAYSIZE];
int InitBuff()
{
int i = 0;
for(; i < ARRAYSIZE; ++i)
{
Buff[i] = -1;
}
}
void AddBuff(int fd);
{
int i = 0;
for(; i < ARRAYSIZE; ++i)
{
if(Buff[i] == -1)
{
Buff[i] = fd;
return;
}
}
printf("Buff Full\n");
}
//获取一个有效的文件描述符并将Buff中的位置置为-1
int GetBuff()
{
int i = 0;
for(; i < ARRATSIZE; ++i)
{
if(Buff[i] != -1)
{
int c = Buff[i];
Buff[i] = -1;
return c;
}
}
return -1;
}
//所有函数线程的主逻辑: 1、为一个客户端服务 2、处理一次就绪事件
void * work_pthread(void *arg)
{
while(1)
{
sem_wait(&sem);//信号量的P操作
}
int fd = GetBuff();
if(fd == -1)
{
printf("GetBuff error\n");
continue;
}
char buff[128] = 0;
int n = recv(fd, buff, 127, 0);
if(n <= 0)
{
printf("recv error\n");
continue;
}
printf("%d: %s\n", fd, buff);
send(fd, "OK", 2,0);
}
void CreatePthread()
{
int i = 0;
for(; i < PTHREADNUM; ++i)
{
int res = pthread_create(&id[i],NULL, work_pthread, NULL);
assert(res == 0);
}
}
int CreateSocket()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd == -1)
return -1;
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(6000);
ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(listenfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if(res == -1) return -1;
res = listen(listenfd, 5);
if(res == -1) return -1;
return listenfd;
}
int GetNewLink(int fd, int epfd)
{
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
int c = accept(fd, (struct sockaddr*)&cli_addr, &len);
if(c == -1)
{
printf("Get new link error\n");
return;
}
printf("Get new link success\n");
struct epoll_event.event;
event.data.fd = c;
event.events = EPOLLIN | EPOLLRDHUP;
int res = epoll_ct(epfd, EPOLL_CTL_ADD, c, &event);
}
void CloseLink(int fd, int epfd)
{
int res = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
assert(res != -1)
close(fd);
}
int DealReadEvent(int epfd, struct epoll_event *events, int n, int listenfd)
{
int i = 0;
for(; i < n;i++)
{
int fd = events[i].data.fd;
if(fd == listenfd)
{
GetNewLink(fd, epfd);
}
else
{
if(events.events & EPOLLRDHUP)
{
CloseLink(fd, epfd);
}
else
{
AddBuff(fd);
sem_post()
}
}
}
}
int main()
{
sem_init(&sem, 0, 0);
InitBuff();
CreatePthread();
int listenfd = CreateSocket();
assert(listenfd != -1);
int epfd = epoll_create(5);
assert(epfd != -1);
struct epoll_event.event;
event.data.fd = listenfd;
event.events = EPOLLIN;
int res = epoll_ct(epfd, EPOLL_CTL_ADD, listenfd, &event);
assert(res != -1);
while(1)
{
struct epoll_event events[EVENTSIZE];
int n = epoll_wait(epfd, events,EVENTSIZE.-1);
if(n<=0)
{
printf("epoll_wait error\n");
break;
}
DealReadEvent(epfd, events, n, listenfd);
}
exit(0);
}