IO多路复用中的水平触发和边缘触发、Java NIO中的水平触发举例

news2024/12/25 9:18:35

基础原理

水平触发(Level-triggered,也被称为条件触发)LT:主要满足条件,就触发事件。

边缘触发(Edge-triggered)ET:当状态变化时触发。

        使用脉冲信号来说明LT和ET:LT指信号只需要处于水平(高电平、低电平)就会一直触发;ET则是指信号为上升沿或下降沿时才会触发通知。

        举例说明:一个管道收到了1kb的数据,epoll会立即返回,此时读了512字节数据,然后再次调用epoll。这时如果是水平触发的,epoll会立即返回,因为有数据准备好了。如果是边缘触发的不会立即返回,因为此时虽然有数据可读但是已经触发了一次通知,在这次通知到现在还没有新的数据到来,直到有新的数据到来epoll才会返回,此时老的数据和新的数据都可以读取到(当然是需要这次你尽可能的多读取)。

        在水平触发的情况下,必须不断的轮询监控每个文件描述符的状态,判断其是否可读或可写。内核空间中维护的 I/O 状态列表可能随时会被更新,因此用户程序想要拿到 I/O 状态列表必须访问内核空间。
        而边缘触发的情况下,只有在数据到达网卡,也就是说 I/O 状态发生改变时才会触发事件,在两次数据到达的间隙,I/O 状态列表是不会发生改变的。这就使得用户程序可以缓存一份 I/O 状态列表在用户空间中,减少系统调用的次数。 

        但是在边缘触发的情况下,I/O 操作必须一次性的将数据处理完。因为如果没有处理完数据,只有等待下次数据包到达网卡才会再次触发事件。
        在水平触发的情况下,可以处理内核缓冲区中任意长度的数据。如果数据没有处理完,内核会再次触发事件。因此剩余数据在下次事件到来时继续处理即可。
        水平触发和边缘触发都需要非阻塞读写。因为它们都属于多路复用技术的实现方式,而使用多路复用技术的触发点便是用更少的线程做更多的事。单线程情况下,无论水平触发还是边缘触发,使用阻塞读写都会造成线程无法处理其它事件的情况。

水平触发和边缘触发的触发时机

水平触发

  • 对于读操作:只要缓冲内容不为空,LT模式返回读就绪。
  • 对于写操作:只要缓冲区内容还不满,ET模式会返回写就绪。

边缘触发

  • 对于读操作:
    • 当缓冲区内容由不可读变为可读的时候,即缓冲区由空变为不为空的时候。
    • 当缓冲区有新数据达到时,即缓冲区中的待读数据变多时。
    • 当缓冲区有数据可读,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLIN事件时。
  • 对于写操作:
    • 当缓冲区由不可写变为可写时。
    • 当有旧数据被发送走时,即缓冲区中的内容变少时。
    • 当缓冲区有空间可写时,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLOUT事件时。

Java NIO多路复用技术中的水平触发

        JAVA 的 NIO 属于水平触发,而 epoll 既支持水平触发也支持边缘触发。epoll 性能高于 poll 很重要的一点便是 epoll 支持了边缘触发。

        代码示例:

Server:

package netty.netProgram.trigger;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * 验证Java NIO中的水平触发
 */
@Slf4j
public class LevelTrigger {
    public static void main(String[] args) throws IOException {
        server();
    }
    private static void server() throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel severChannel = ServerSocketChannel.open();
        severChannel.configureBlocking(false);
        severChannel.bind(new InetSocketAddress(8888));
        System.out.println("Server start!");
        severChannel.register(selector, SelectionKey.OP_ACCEPT);
        int count = 0;
        //select会阻塞,知道有就绪连接写入selectionKeys
        while (!Thread.currentThread().isInterrupted()) {
            if (selector.select(100) == 0) {
                continue;
            }
            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
            while (keys.hasNext()) {
                //SelectionKey为select中记录的就绪请求的数据结构,其中包括了连接所属的socket及就绪的类型
                SelectionKey key = keys.next();
                //处理事件,不管是否可以处理完成,都删除 key。因为 soketChannel 为水平触发的,
                // 未处理完成的事件删除后会被再次通知
                keys.remove();
                if (key.isAcceptable()) {
                    System.out.println("触发连接事件");
                    SocketChannel socketChannel = severChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(8);
                    int len = socketChannel.read(byteBuffer);
                    byteBuffer.flip();
                    if (len == -1) {
                        key.cancel();
                        socketChannel.close();
                    }
                    count += len;
                    log.debug("current receive data len is : {}, total receive data len is :{}", len, count);
                }
            }
        }
    }
}

Client:

package netty.netProgram.trigger;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

@Slf4j
public class LevelTriggerClient {
    public static void main(String[] args) {
        try {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 100000; i++) {
                sb.append("a" + i % 26);
            }
            ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());
            socketChannel.write(buffer);
            log.debug("client send data len is : {}", sb.toString().length());
            while (true){
                // 死循环,维持客户端和服务端的链接,使得服务端能够完全读取数据
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

        结果演示:

参考链接

I/O多路复用中的水平触发和边缘触发_nio水平触发和边缘触发的区别-CSDN博客

epoll 水平触发与边缘触发_epoll水平触发-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nginx--地址重写Rewrite

一、什么是Rewrite Rewrite对称URL Rewrite&#xff0c;即URL重写&#xff0c;就是把传入Web的请求重定向到其他URL的过程 URL Rewrite最常见的应用是URL伪静态化&#xff0c;是将动态页面显示为静态页面方式的一种技术。比如http://www.123.com/news/index.php?id123 使用U…

Linux命令之一

Linux命令之一 帮助类命令磁盘管理文件管理系统设置开关服务命令临时开关服务命令永久开关服务命令 压缩/解压网络通讯网络访问管道和重定向搜索命令grepfind 磁盘分区类命令 Linux命令速查平台 帮助类命令 语法 man [命令或配置文件] &#xff08;功能描述&#xff1a;获得帮助…

【c++】深入理解别名机制--引用

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C 目录 前言 一、引用的概念和定义 二、引用的特性 三、引用的实用性 1.引用传参 2.引用做返回值 2.1 引用做返回值的作用 2.2 引用坍缩问题、悬挂引用问…

ThreadPoolExecutor详解

恰逢经济下行&#xff0c;鄙人工作、生活日趋艰难&#xff0c;原本美好的愿望&#xff0c;如今只能成为奢望。不知如何是好的我&#xff0c;只能精研近几年来因浮躁而荒废的知识。今天就想跟大家聊一个对我来讲看似熟悉实则陌生的工具——ThreadPoolExecutor。熟悉是因为在我负…

【nacos 第二篇章】动手实践(从零代码开发版)

一、环境准备 本章将通过手把手的教程一步一步教你如何从零开发一个微服务应用。 首先需要安装好 nacos 服务并启动。安装 nacos 服务请看作者的 【nacos 第一篇章】安装一下 nacos 文章。 二、初始化项目 如上图所示&#xff0c;可以建立一个基础的项目。 搭建了基础项目之…

计算机毕业设计 在线项目管理与任务分配系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

节点使用简介:comfyui-photoshop

1、安装comfyui-photoshop 略过 一点要注意的是&#xff1a;在Photoshop上的安装增效工具&#xff0c;要通过Creative Cloud 桌面应用程序进行安装&#xff0c;才能成功在增效工具中显示&#xff0c;直接通过将文件解压到Plug-ins路径行不通&#xff08;至少对我来说行不通&am…

通过剪枝与知识蒸馏优化大型语言模型:NVIDIA在Llama 3.1模型上的实践与创新

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

阿里云ACP的三种报名与对应题库获取方式的详细说明(按费用排序)

文章目录 前言方式一、官方途径(较为昂贵)考试资格获取官方视频教程获取方式总结 方式二、报名机构(价格适中&#xff0c;考取速度快)推荐机构大概费用机构报名方式机构报名后所携带的内容或者说对于其他方法有什么优势总结 方式三、闲鱼(最便宜&#xff0c;但题库有风险)考试资…

C语言 之 strstr函数的使用和模拟、strtok函数的使用、strerror函数和perror函数的使用

文章目录 strstr 的使用和模拟实现strstr函数模拟实现 strtok 函数的使用例子1例子2例子3 strerror 函数的使用perror函数 strstr 的使用和模拟实现 函数原型&#xff1a; const char * strstr ( const char * str1, const char * str2 ); 该函数能够查找str1中第一次出现str2…

产业经济大脑建设方案(五)

为了提升产业经济的智能化水平&#xff0c;我们提出建设一个综合产业经济大脑系统&#xff0c;该系统通过整合大数据分析、人工智能和云计算技术&#xff0c;构建全方位的数据采集、处理和决策支持平台。该平台能够实时监测产业链各环节的数据&#xff0c;运用智能算法进行深度…

Unified 阻抗控制 architecture、framework、approach

Unified 阻抗控制&#xff08;Unified Impedance Control&#xff09;作为一种控制策略&#xff0c;其architecture&#xff08;架构&#xff09;、framework&#xff08;框架&#xff09;和approach&#xff08;方法&#xff09;为&#xff1a; 一、Unified 阻抗控制 Archite…

【MPC】模型预测控制 | 车辆优化控制策略研究与实践

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

CorelDRAW X4重磅发布更新!包含最新安装包下载

CorelDRAW X4是矢量图形制作工具软件&#xff0c;这个图形工具给设计师们提供了矢量动画、页面设计、网站制作、位图编辑和网页动画等多种功能。 X4版本的新特性有活动文本格式&#xff1a;仿效现有段落格式化、使文本能够贴齐格线框&#xff0c;或从边框缩排&#xff0c;从而获…

牛客JS题(四十四)根据包名,在指定空间中创建对象

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 地址引用assign 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><style>/* 填写样式 */</style></head><body><!--…

使用多种机器学习模型进行情感分析

使用 TF-IDF 与贝叶斯分类器进行情感分析是一个常见且有效的组合&#xff0c;特别是在文本分类任务中。贝叶斯分类器&#xff08;通常是朴素贝叶斯分类器&#xff09;等机器学习模型具有计算简单、效率高的优点&#xff0c;且在文本分类任务中表现良好。接下来&#xff0c;我将…

8.16作业

1.思维导图 2.在登录界面的登录取消按钮进行以下设置&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断…

Solidworks二次开发:通过XYZ点的曲线

在SolidWorks中,通过XYZ点创建曲线是一种根据一组点的坐标生成三维曲线的方法。这种方法适用于需要根据特定点集设计曲线的情况,比如在建模复杂几何形状或执行逆向工程时。在SolidWorks中通过XYZ点创建曲线,操作步骤如下 打开SolidWorks并新建文件:启动SolidWorks软件,新建…

开放式耳机哪个品牌好用?盘点四款开放式蓝牙耳机排行榜前十名

作为网易云十级选手&#xff0c;测评过三十多款开放式耳机产品的开放式耳机测评专家来说&#xff0c;如果想要入手一款好用的开放式耳机的话&#xff0c;我会建议从三个方面来考虑&#xff0c;佩戴体验、音质效果和性能配置&#xff0c;了解这三个方面选购款好用的开放式耳机不…

Linux配置JDK8环境变量

目录 一、yum安装1.1 OpenJDK安装1.2 测试是否能够使用1.3 如何卸载JDK 二、手动安装2.1 下载2.2 上传到linux服务器路径2.3 解压2.4 配置环境变量2.5 测试是否能够使用 一、yum安装 1.1 OpenJDK安装 sudo yum install -y java-1.8.0-openjdk-devel1.2 测试是否能够使用 jav…