博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
epoll的LT和ET使用EPOLLONESHOT
阅读量:6138 次
发布时间:2019-06-21

本文共 2734 字,大约阅读时间需要 9 分钟。

epoll有两种触发的方式即LT(水平触发)和ET(边缘触发)两种,在前者,只要存在着事件就会不断的触发,直到处理完成,而后者只触发一次相同事件或者说只在从非触发到触发两个状态转换的时候儿才触发。

这会出现下面一种情况,如果是多线程在处理,一个SOCKET事件到来,数据开始解析,这时候这个SOCKET又来了同样一个这样的事件,而你的数据解析尚未完成,那么程序会自动调度另外一个线程或者进程来处理新的事件,这造成一个很严重的问题,不同的线程或者进程在处理同一个SOCKET的事件,这会使程序的健壮性大降低而编程的复杂度大大增加!!即使在ET模式下也有可能出现这种情况!!

解决这种现象有两种方法:

第一种方法是在单独的线程或进程里解析数据,也就是说,接收数据的线程接收到数据后立刻将数据转移至另外的线程。

第二种方法就是本文要提到的EPOLLONESHOT这种方法,可以在epoll上注册这个事件,注册这个事件后,如果在处理写成当前的SOCKET后不再重新注册相关事件,那么这个事件就不再响应了或者说触发了。要想重新注册事件则需要调用epoll_ctl重置文件描述符上的事件,这样前面的socket就不会出现竞态这样就可以通过手动的方式来保证同一SOCKET只能被一个线程处理,不会跨越多个线程。

看下面的代码:

void Eepoll::ResetOneShot(int  epollfd,SOCKET fd,bool bOne){         epoll_eventevent;         event.data.fd= fd;         event.events= EPOLLIN | EPOLLET ;         if(bOne)         {                   event.events |=EPOLLONESHOT;         }         if(-1 == epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event))         {                   perror("resetoneshotepoll_ctl error!");         }}

  

这里有一个问题,在操作ET模式下的EPOLL时,对EPOLLONESHOT没有什么太大的注意点,但是在LT时,就有一些注意的了。

前面说过LT会不断触发,所以在处理数据时,不需要在RECV时不断的循环去读一直读到EAGAIN,但如果设置了EPOLLONESHOT后,也得如此办理,否则,就可能会丢掉数据。一个采用EPOLLONETSHOT的例子:

epoll_oneshot._server.cpp服务端程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_EVENT_NUMBER 1024//最大事件连接数 #define BUFFER_SIZE 1024//接收缓冲区大小 using namespace std; struct fds{//文件描述符结构体,用作传递给子线程的参数 int epollfd; int sockfd; }; int setnonblocking(int fd){//设置文件描述符为非阻塞 int old_option=fcntl(fd,F_GETFL); int new_option=old_option|O_NONBLOCK; fcntl(fd,F_SETFL,new_option); return old_option; } void addfd(int epollfd,int fd,bool oneshot){//为文件描述符添加事件 epoll_event event; event.data.fd=fd; event.events=EPOLLIN|EPOLLET; if(oneshot){//采用EPOLLONETSHOT事件 event.events|=EPOLLONESHOT; } epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); setnonblocking(fd); } void reset_oneshot(int epollfd,int fd){//重置事件 epoll_event event; event.data.fd=fd; event.events=EPOLLIN|EPOLLET|EPOLLONESHOT; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event); } void* worker(void* arg){//工作者线程(子线程)接收socket上的数据并重置事件 int sockfd=((fds*)arg)->sockfd; int epollfd=((fds*)arg)->epollfd;//事件表描述符从arg参数(结构体fds)得来 cout<<"start new thread to receive data on fd:"<
<
=0); ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address)); assert(ret!=-1); ret=listen(listenfd,5); assert(ret!=-1); epoll_event events[MAX_EVENT_NUMBER]; int epollfd=epoll_create(5); assert(epollfd!=-1); addfd(epollfd,listenfd,false);//不能将监听端口listenfd设置为EPOLLONESHOT否则会丢失客户连接 while(1){ int ret=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);//等待事件发生 if(ret<0){ cout<<"epoll error"<

 

转载地址:http://ggkya.baihongyu.com/

你可能感兴趣的文章
OC 单例实现
查看>>
计算日期相隔天数/根据天数差计算结束日期
查看>>
iOS中RAC的使用
查看>>
MongonDB入门
查看>>
leetcode-506-Relative Ranks
查看>>
高并发编程必备基础(上)
查看>>
客户端HTTP缓存
查看>>
mysql server安装及配置
查看>>
一步一步学习SignalR进行实时通信_5_Hub
查看>>
[译] JavaScript 简明代码 —— 最佳实践
查看>>
win8.1恢复win7 CTRL+Space切换输入法
查看>>
强大的PHP一句话后门
查看>>
jquery中的prop方法和attr方法
查看>>
OWASP Top 10 2017年
查看>>
10.28T7 位运算+记忆化+DLZ常数剪枝
查看>>
并发之无锁技术归纳
查看>>
Linux用户名显示-bash-4.1$快速排查
查看>>
cocoapods安装与使用
查看>>
spring-boot + mybatis +pagehelper 使用分页
查看>>
【SSH网上商城项目实战04】EasyUI菜单的实现
查看>>