水平触发:
Level_triggered(水平触发):当被监控的文件描述符(fd)上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!
边缘触发:
Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!
来源:https://zhuanlan.zhihu.com/p/532789377
场景:
基于gsoap服务;处理短暂的HTTP请求;但是请求非常频繁;需要多线程处理设备发现请求和HTTP接口请求
写法1:
gsoap 服务主要做设备发现的响应和onvif服务的响应;启动任务,启动两条常驻线程(或者线程池启两条线程,在线程任务task里while循环检测是否有socket链接),时刻检测两个fd(设备发现的socket 发的,和http请求的tcp socket fd)上是否有链接请求;soap_server;
1的问题:
使用普通线程常驻:设备发现(加入多播组的UDP socket)也好,onvif的接口请求(HTTP请求,tcp连接)都是短连接,如果客户端多,并且请求频繁的话,线程的真实使用率较高,但是如果只是偶尔几个客户端请求,常驻线程大部分时间都没做事;
使用线程池+while循环:这样做虽然是在使用线程池处理短连接任务,但是为了避免错过任何客户端请求,while循环必须一直运行,也就变相的将线程池中处理短任务的线程,变成长期占用;现成池中的线程长期不能停歇;
基于此,如果服务是多端口的,即进行HTTP响应的端口不止430;(常用的onvif服务都是单端口,但是也可以一个IP加入多播组,多个端口接收设备发现信息,并各自响应onvif请求,客户端发送onvif协议交互,可选择端口)
上边1的开发逻辑,要么常驻N条线程,要么出现线程池中多条线程,并且由于有些线程池是notify_one 的,当一下启动多条线程池任务时候,很可能导致有些任务永远不会被执行(因为线程池中的线程一直被while循环占用,线程池中的task是长任务)
综上:要么占用非常多的线程,要么就导致任务无法被处理;基于此,既要解放线程池,又不能错误任何客户端过来的请求;
写法2:EVENT pool (水平触发)+线程池
单独的线程(开一个普通线程处理 event pool 的wait)处理event pool 的wait事件;当接收到 读写事件的时候存入线程池中的task队列;注意区分读写事件;并不是所有类型事件都需要监听,客户端过来的链接只需要监听读事件即可;
event pool 开水平触发模式;
问题:
当启动多个端口,并且有多个客户端过来链接本服务时候,由于wait到的fd的处理事件,是放到任务队列中等待线程池消耗的;就导致任务不是第一时间处理了;没有第一时间wait到就读socket缓冲,根据(“如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读”)就会出现一个wait到的fd的事件一直重复;除非能立即消耗掉fd的这个读写事件,否则会频繁被event_pool wait到,导致线程池的任务队列也会爆慢,任务都是重复的;
这就说明水平触发模式获取到的任务,你需要立即执行;
写法3: EVENT pool (边缘触发)+线程池
单独的线程处理event pool 的wait事件((开一个普通线程处理 event pool 的wait));当接收到 读写事件的时候存入线程池中的task队列;注意区分读写事件;并不是所有类型事件都需要监听,客户端过来的链接只需要监听读事件即可;
//#if defined(_WIN32) // unsigned long ul = 1; // int ret1 = ioctlsocket(sockfd, FIONBIO, &ul); //#else // int ul = 1; // int ret1 = ioctl(sockfd, FIONBIO, &ul); //#endif //defined(_WIN32)//ev.events = EPOLLIN| EPOLLET; ///ev.events = EPOLLIN;//默认水平触发
问题:
当有多个客户端都在访问一个gsoap 的onvif服务的时候;只会通知一次,wait到一次;虽然可以免去重复任务被wait到,但是可能当前fd有多个读写事件,每个事件来自不同的客户端,所以这里如果你无法把本次wait到的FD,的内核中的读写事件全部处理完,就会造成有些事件未能及时处理,就会造成,客户端的请求无法及时回复,甚至是下次发请求,回复上一次的请求响应;如图,需要一次性将客户端1,2,5对fd21的请求处理完;也就是socket读尽;一次解析多条请求,分别放入任务队列;
写法4:水平触发+阻塞任务处理+线程池
使用水平触发,为了避免任务重复,必须等到线程队列中将当前fd的这个当前任务处理完,在wait(接收)本fd的事件;
如:wait到fd =1;的来自客户端1的请求,放入任务队列,记录fd1有待处理任务,等线程池执行完fd=1的任务,标记处理完成;在fd=1;被标记为处理完前wait到的fd=1的事件,全部丢弃;直到处理完fd=1来自客户端1的任务;以此类推,避免重复,又能将多个客户端的任务处理完
标签:请求,读写,客户端,线程,应用,fd,poll,Event,wait From: https://www.cnblogs.com/8335IT/p/18676581