17.2 条件触发 和 边缘触发
2026/1/8大约 3 分钟
17.2 条件触发 和 边缘触发
相关信息
- 条件触发方式中,只要输入缓冲有数据就会一直通知该事件
- 边缘触发方式中,收到数据时仅注册1次该事件
- 条件触发 和 边缘触发的区别在于发生事件的时间点
| 触发方式 | |
|---|---|
| 条件触发 | 输入缓冲中有数据 |
| 边缘触发 | 收到数据 - 输入缓冲新增数据 |
不管是条件触发 还是 边缘触发,这两种触发方式产生的事件都表示输入缓冲有数据要处理。差别在于,在后续处理过程中,如果没有一次性读取完所有数据,那么由条件触发的事件本身就表示输入缓冲还有数据,而边缘触发的事件只有一次,后续需要通过其他方式判断输入缓冲时候还有数据。
事件特性
观察两种触发方式产生的事件的数量
设置触发方式
标志定义:
EPOLLET
Sets the Edge Triggered behavior for the associated file descriptor. The default behavior for epoll is Level Triggered.代码:
if (events[n].data.fd == listen_sock) {
{
//...
ev.events = EPOLLIN | EPOLLET;
//...
}
else
{
int r = recv(events[n].data.fd, message, sizeof(message), MSG_PEEK);
//...
}读取数据时不从输入缓冲中删除数据
效果
- 边缘触发
ming@ubuntu:/media/sf_share/Network/build$ ./Network 10086
Socket created, listen_sock fd:3
Number of events: 1
n:0 event:1, fd=3
Connection established with client, client fd:5, ip:192.168.56.1,port:5852
Number of events: 1
n:0 event:1, fd=5
Received message: 123收到3字节数据,但是只有一个事件
- 条件触发
Received message: 123
Number of events: 1
n:0 event:1, fd=5
Received message: 123
Number of events: 1由于输入recv + MSG_PEEK 不会删除输入缓冲中的数据,导致输入缓冲一直存在数据,所以一直产生事件,程序陷入死循环。
边缘触发的服务器端实现
- 非阻塞 I/O
- I/O 函数返回时,判断是输入为空还是发生错误 或 读取到数据
实现:
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
//...
}
else
{
while(1)
{
int r = recv(events[n].data.fd, message, sizeof(message), MSG_PEEK);
if(r == 0)
;//close request
else if( r < 0)
{
if(errno == EAGAIN)
break;
}
else
{
//..
}
}
}
void setnonblocking(int fd)
{
int flag = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);
}| 行号 | 功能 | 说明 |
|---|---|---|
| 32 - 36 | 设置非阻塞I/O | F_GETFL 用来获取fd的当前设置,F_SETFL 用来设置。 |
| 19 - 23 | read错误处理 | read返回 -1,errno 为 EAGAIN,说明数据可读 |
效果:
略条件触发 和 边缘触发 孰优孰劣
相关信息
边缘触发可以做到:
可以分离接收数据和处理数据的时间点
应用情景
- 服务端分别从A、B、C 客户端接收数据
- 服务端按照A、B、C的顺序组合接收数据
- 组合后的数据发送给D
问题:
- A、B、C连接、发送的顺序不确定:需要等待A、B、C全部连接且发送数据后进行组合
- D的连接不确定:已经组合的数据需要等待D连接后再发送给D
使用边缘触发,在判断条件满足后再从输入缓冲中读取数据。可以等待条件全部满足,也可以等待部分条件满足,如输入满足 - A、B、C全部已经发送数据,那么就按顺序读,然后暂存在临时缓冲中。等待D连接后直接发送。
我的理解:
- 使用边缘触发的方式,可以让数据暂时留存在输入缓冲中,等待条件满足后再从输入缓冲中读取数据。
- 条件触发:不能分离接收数据和处理数据的时间点。只要有数据就要读取,否则一直产生事件。相对应的没办法利用输入缓冲。
相关信息
条件触发和边缘触发的区别主要应该从服务端实现模型的角度谈论。
更应该关注触发方式对应用实现的影响,而不是性能。
问:这里的性能是事件产生的速度?