面完字节终于有空整理一下笔记🤦
为什么使用线程池
每个请求对应一个线程方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在 创建和销毁 线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。
-
提高响应速度:任务到达时,无需等待线程即可立即执行;
-
提高线程的可管理性:线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
怎么创建线程池/线程池运行逻辑
使用线程池(半同步半反应堆模式)并发处理用户请求,主线程负责读写,工作线程(线程池中的线程)负责处理逻辑(HTTP请求报文的解析等) 。
具体来说,主线程为异步线程,负责监听文件描述符,接收socket新连接,若当前监听的socket发生了读写事件,然后将任务插入到请求队列。工作线程从请求队列中取出任务,完成读写数据的处理。
线程池是空间换时间,浪费服务器的硬件资源,换取运行效率.
线程的同步机制有哪些
(1)同步I/O
同步I/O指内核向应用程序通知的是就绪事件,比如只通知有客户端连接,要求用户代码自行执行I/O操作
a) 阻塞IO: 调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作
b) 非阻塞IO: 非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。非阻塞I/O执行系统调用总是立即返回,不管时间是否已经发生,若时间没有发生,则返回-1,此时可以根据errno区分这两种情况,对于accept,recv和send,事件未发生时,errno通常被设置成eagain
c) 信号驱动IO: linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
d) IO复用: linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
(2)异步I/O
异步I/O是指内核向应用程序通知的是完成事件,比如读取客户端的数据后才通知应用程序,由内核完成I/O操作
linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
线程池中的工作线程是一直等待吗?
在run函数中,我们为了能够处理高并发的问题,将线程池中的工作线程都设置为阻塞等待在请求队列是否不为空的条件上,因此项目中线程池中的工作线程是处于 一直阻塞等待 的模式下的。
5、你的线程池工作线程处理完一个任务后的状态是什么?
(1) 当处理完任务后如果请求队列为空时,则这个线程重新回到阻塞等待的状态
(2) 当处理完任务后如果请求队列不为空时,那么这个线程将处于与其他线程竞争资源的状态,谁获得锁谁就获得了处理事件的资格。
如果同时1000个客户端进行访问请求,线程数不多,怎么能及时响应处理每一个呢?
本项目是通过 对子线程循环调用来解决高并发 的问题的。
首先在创建线程的同时就调用了pthread_detach将线程进行分离,不用单独对工作线程进行回收,资源自动回收。
我们通过子线程的run调用函数进行while循环,让每一个线程池中的线程永远都不会停止,访问请求被封装到请求队列(list)中,如果没有任务线程就 一直阻塞等待,有任务线程就 抢占式进行处理,直到请求队列为空,表示任务全部处理完成。
如果一个客户请求需要占用线程很久的时间,会不会影响接下来的客户请求呢,有什么好的策略呢?
会,因为线程池内 线程的数量时有限 的,如果客户请求占用线程时间过久的话会影响到处理请求的 效率,当请求处理过慢时会造成后续接受的请求只能在请求队列中等待被处理,从而影响接下来的客户请求。
应对策略:
我们可以为线程处理请求对象 设置处理超时时间,超过时间先发送信号告知线程处理超时,然后设定一个时间间隔再次检测,若此时这个请求还占用线程则直接将其断开连接。