Flink1.14.* 各种算子在StreamTask控制下如何调用的源码

news2024/11/22 14:21:25

  • 前言:
  • 一、StreamTask执行算子的生命周期
  • 二、 Source的streamTask用的是SourceStreamTask
  • 三、基础转换操作,窗口用的是OneInputStreamTask
    • 1、初始化OneInputStreamTask
    • 2、StreamTask运行invoke调用的是StreamTask的processInput方法
    • 3、从缓冲区获取数据放入到内存中
    • 4、调用算子的processElement方法处理数据,
  • 四、sink的streamTask用的也是OneInputStreamTask
  • 五、OneInputStreamTask和SourceStreamTask类关系图

前言:

在 Apache Flink 中,StreamTask 类是处理流数据的核心执行单元。
它负责管理算子的生命周期,并调用算子的处理方法。StreamTask 类的全路径(即完整的包名和类名)如下:
StreamTask 类位于 flink-streaming-java 模块中,具体的包结构为 org.apache.flink.streaming.runtime.tasks
全路径如下

flink-streaming-java/src/main/java/org/apache/flink/streaming/runtime/tasks/StreamTask.java

一、StreamTask执行算子的生命周期

先看StreamTask大体执行流程(忽略实现类的细节)

public abstract class StreamTask<OUT, OP extends StreamOperator<OUT>> implements TaskInvokable, CheckpointableTask, CoordinatedTask, AsyncExceptionHandler, ContainingTaskDetails {
    protected OP mainOperator;
    private boolean mailboxLoopRunning;

    //第一步构造函数,把processInput赋值给mailboxProcessor
    protected StreamTask(Environment environment, @Nullable TimerService timerService, UncaughtExceptionHandler uncaughtExceptionHandler, StreamTaskActionExecutor actionExecutor, TaskMailbox mailbox) throws Exception {
        this.mailboxProcessor = new MailboxProcessor(this::processInput, mailbox, actionExecutor);
        //默认为true
        this.mailboxLoopRunning = true;

    }
    //第三步StreamTask执行
    public final void invoke() throws Exception {
        //省略代码
        this.runMailboxLoop(); 
    }
    //SourceStreamTask会重写这个方法,OneInputStreamTask不会重写
    protected void processInput(Controller controller) throws Exception {
        //删除干扰代码,
    }
}
public class MailboxProcessor implements Closeable {
    protected final MailboxDefaultAction mailboxDefaultAction;
    //第二步 构造函数把processInput方法赋值给mailboxDefaultAction
    public MailboxProcessor(MailboxDefaultAction mailboxDefaultAction, TaskMailbox mailbox, StreamTaskActionExecutor actionExecutor) {
        //这里mailboxDefaultAction传的是this::processInput
        this.mailboxDefaultAction = (MailboxDefaultAction)Preconditions.checkNotNull(mailboxDefaultAction);
    }
    //第四步,
    public void runMailboxLoop() throws Exception {
        //suspended默认是false
        this.suspended = !this.mailboxLoopRunning;
        //this.isNextLoopPossible默认是true
        while(this.isNextLoopPossible()) {
            this.mailboxProcessor.runMailboxLoop();
        }
    }
    private boolean isNextLoopPossible() {
        return !this.suspended;
    }

    //第五步,调用processInput
    public void runMailboxLoop() throws Exception {
        //这个执行的是processInput方法
        this.mailboxDefaultAction.runDefaultAction(defaultActionContext);
    }
}

不同的实现类都是按照上面的步骤初始化执行的

二、 Source的streamTask用的是SourceStreamTask

@Internal
public class SourceStreamTask<OUT, SRC extends SourceFunction<OUT>, OP extends StreamSource<OUT, SRC>> extends StreamTask<OUT, OP> {

    private final SourceStreamTask<OUT, SRC, OP>.LegacySourceFunctionThread sourceThread;

    protected void init() {
        //这个mainOperator是StreamTask的字段,,
        SourceFunction<?> source = (SourceFunction)((StreamSource)this.mainOperator).getUserFunction();
    }    

    protected void processInput(Controller controller) throws Exception {
        //这里启动线程的run方法
        this.sourceThread.start();
        
    }

    private class LegacySourceFunctionThread extends Thread {
        private final CompletableFuture<Void> completionFuture = new CompletableFuture();

        LegacySourceFunctionThread() {
        }

        public void run() {
            try {
                if (!SourceStreamTask.this.operatorChain.isTaskDeployedAsFinished()) {
                    StreamTask.LOG.debug("Legacy source {} skip execution since the task is finished on restore", SourceStreamTask.this.getTaskNameWithSubtaskAndId());
                    ((StreamSource)SourceStreamTask.this.mainOperator).run(SourceStreamTask.this.lock, SourceStreamTask.this.operatorChain);
                }
                //删除干扰代码
            } catch (Throwable var2) {
                //删除干扰代码
            }

        }
    }
}

第一点需要注意的是由于SourceStreamTask重写了streamTaskprocessInput方法,所以streamTaskinvoke方法执行的是子类的SourceStreamTaskprocessInput方法

第二点看一下init方法,这里(SourceFunction)((StreamSource)this.mainOperator).getUserFunction()就是获取的source算子,不清楚的可以看一下kafkaSource这篇文章Flink 1.14.*版本kafkaSource源码
由这里来触发SourceFunctionrun方法,即FlinkKafkaConsumerBaserun方法

三、基础转换操作,窗口用的是OneInputStreamTask

这种一般都是中间算子,或者最后一个算子(例如kafkaSink),所以主要涉及到从输入源获取数据,处理数据,并将结果写入输出中
如果连着看下面两篇文章,你就会知道为什么sink也是用的OneInputStreamTask
Flink 1.14.*中flatMap,filter等基本转换函数源码
Flink 1.14.* 版本kafkaSink源码

1、初始化OneInputStreamTask

public class OneInputStreamTask<IN, OUT> extends StreamTask<OUT, OneInputStreamOperator<IN, OUT>> {
    public void init() throws Exception {
        //output是私有类StreamTaskNetworkOutput对象
        DataOutput<IN> output = this.createDataOutput(numRecordsIn);
        StreamTaskInput<IN> input = this.createTaskInput(inputGate);
        //这个inputProcessor字段是给父类StreamTask初始化的,这时候父类inputProcessor=StreamOneInputProcessor
        this.inputProcessor = new StreamOneInputProcessor(input, output, this.operatorChain);
    }
    private StreamTaskInput<IN> createTaskInput(CheckpointedInputGate inputGate) {
        int numberOfInputChannels = inputGate.getNumberOfInputChannels();
        StatusWatermarkValve statusWatermarkValve = new StatusWatermarkValve(numberOfInputChannels);
        TypeSerializer<IN> inSerializer = this.configuration.getTypeSerializerIn1(this.getUserCodeClassLoader());
        return StreamTaskNetworkInputFactory.create(inputGate, inSerializer, this.getEnvironment().getIOManager(), statusWatermarkValve, 0, this.getEnvironment().getTaskStateManager().getInputRescalingDescriptor(), (gateIndex) -> {
            return ((StreamEdge)this.configuration.getInPhysicalEdges(this.getUserCodeClassLoader()).get(gateIndex)).getPartitioner();
        }, this.getEnvironment().getTaskInfo());
    }
    //返回的是下面私有类StreamTaskNetworkOutput对象
    private DataOutput<IN> createDataOutput(Counter numRecordsIn) {
        return new OneInputStreamTask.StreamTaskNetworkOutput(this.operatorChain.getFinishedOnRestoreInputOrDefault((Input)this.mainOperator), this.inputWatermarkGauge, numRecordsIn);
    }    
    //私有内部类,对应上面init中的output
    private static class StreamTaskNetworkOutput<IN> implements DataOutput<IN> {
        private final Input<IN> operator;
        public void emitRecord(StreamRecord<IN> record) throws Exception {
            //调用的算子的processElement方法
            this.operator.processElement(record);
        }
    }
   
}

这里是调用init初始化,StreamOneInputProcessor一起初始化了

public final class StreamOneInputProcessor<IN> implements StreamInputProcessor {
    private StreamTaskInput<IN> input;
    private DataOutput<IN> output;
    public StreamOneInputProcessor(StreamTaskInput<IN> input, DataOutput<IN> output, BoundedMultiInput endOfInputAware) {
        //此input就是StreamTaskNetworkInput
        this.input = (StreamTaskInput)Preconditions.checkNotNull(input);
        //此output就是OneInputStreamTask里的私有类StreamTaskNetworkOutput对象
        this.output = (DataOutput)Preconditions.checkNotNull(output);
        this.endOfInputAware = (BoundedMultiInput)Preconditions.checkNotNull(endOfInputAware);
    }

    public DataInputStatus processInput() throws Exception {
        DataInputStatus status = this.input.emitNext(this.output);
        //删除干扰代码
        return status;
    }

}

后面看到this.inputProcessor.processInput其实就是调用的上面类的processInput方法

下面简单介绍一下StreamTaskNetworkInputFactory的创建的两种不同的StreamTaskInput,也可以不用看

public class StreamTaskNetworkInputFactory {
    public StreamTaskNetworkInputFactory() {
    }
    //这里只看返回StreamTaskNetworkInput
    public static <T> StreamTaskInput<T> create(CheckpointedInputGate checkpointedInputGate, TypeSerializer<T> inputSerializer, IOManager ioManager, StatusWatermarkValve statusWatermarkValve, int inputIndex, InflightDataRescalingDescriptor rescalingDescriptorinflightDataRescalingDescriptor, Function<Integer, StreamPartitioner<?>> gatePartitioners, TaskInfo taskInfo) {
        return (StreamTaskInput)(rescalingDescriptorinflightDataRescalingDescriptor.equals(InflightDataRescalingDescriptor.NO_RESCALE) ? new StreamTaskNetworkInput(checkpointedInputGate, inputSerializer, ioManager, statusWatermarkValve, inputIndex) : new RescalingStreamTaskNetworkInput(checkpointedInputGate, inputSerializer, ioManager, statusWatermarkValve, inputIndex, rescalingDescriptorinflightDataRescalingDescriptor, gatePartitioners, taskInfo));
    }
}

StreamTaskNetworkInputFlink 中用于从网络接收数据并将其传递给任务处理的基本组件。它实现了 StreamInput 接口,并负责从网络缓冲区中读取数据,将数据反序列化为 StreamRecord,然后传递给下游的处理逻辑。
主要功能:

  1. 从网络接收数据:读取来自上游任务通过网络发送的数据。
  2. 数据反序列化:将接收到的字节数据反序列化为 StreamRecord 对象
  3. 调用下游处理逻辑:将反序列化后的 StreamRecord 对象传递给下游的处理逻辑(如操作符的 processElement 方法)。

RescalingStreamTaskNetworkInputStreamTaskNetworkInput 的一个扩展,用于处理任务重新缩放(rescaling)场景下的数据接收。任务重新缩放是指在运行时动态调整任务并行度,以适应负载变化。RescalingStreamTaskNetworkInput 主要用于确保在重新缩放过程中数据能够正确地重新分配和处理。
主要功能:

  1. 处理重新缩放场景:在任务重新缩放期间,确保数据能够正确地重新分配和处理。
  2. 数据重分配逻辑:在接收数据时,可能需要根据新的并行度进行数据重分配,以确保数据能够被正确处理。
  3. 继承自 StreamTaskNetworkInput:继承了 StreamTaskNetworkInput 的基本功能,同时增加了处理重新缩放场景的逻辑。

这样初始化部分就完成了

2、StreamTask运行invoke调用的是StreamTask的processInput方法

通过上面第一章节介绍StreamTask的,知道StreamTaskinvoke方法最终执行的是processInput方法,因为OneInputStreamTask不像SourceStreamTask重写了processInput方法,所以调用的还是父类StreamTaskprocessInput方法

public abstract class StreamTask<OUT, OP extends StreamOperator<OUT>> implements TaskInvokable, CheckpointableTask, CoordinatedTask, AsyncExceptionHandler, ContainingTaskDetails {
    protected void processInput(Controller controller) throws Exception {
        DataInputStatus status = this.inputProcessor.processInput();
    }
}

这时候this.inputProcessor=StreamOneInputProcessor,调用processInput即调用StreamOneInputProcessorprocessInput方法

//从OneInputStreamTask初始化章节粘贴过来的,方便
public final class StreamOneInputProcessor<IN> implements StreamInputProcessor {
    private StreamTaskInput<IN> input;
    private DataOutput<IN> output;
    public StreamOneInputProcessor(StreamTaskInput<IN> input, DataOutput<IN> output, BoundedMultiInput endOfInputAware) {
        //此input就是StreamTaskNetworkInput
        this.input = (StreamTaskInput)Preconditions.checkNotNull(input);
        //此output就是OneInputStreamTask里的私有类StreamTaskNetworkOutput对象
        this.output = (DataOutput)Preconditions.checkNotNull(output);
        this.endOfInputAware = (BoundedMultiInput)Preconditions.checkNotNull(endOfInputAware);
    }

    public DataInputStatus processInput() throws Exception {
        DataInputStatus status = this.input.emitNext(this.output);
        //删除干扰代码
        return status;
    }

}

StreamOneInputProcessor.processInput中会调this.input.emitNext(this.output),因为构造StreamOneInputProcessor对象时已经赋值

所以processInput方法中DataInputStatus status = this.input.emitNext(this.output) 调用的是StreamTaskNetworkInputemitNext方法;

public final class StreamTaskNetworkInput<T> extends AbstractStreamTaskNetworkInput<T, SpillingAdaptiveSpanningRecordDeserializer<DeserializationDelegate<StreamElement>>> {

}
public abstract class AbstractStreamTaskNetworkInput<T, R extends RecordDeserializer<DeserializationDelegate<StreamElement>>> implements StreamTaskInput<T> {
    
    //从缓冲区读取到当前内存中
    private R currentRecordDeserializer = null;

    public DataInputStatus emitNext(DataOutput<T> output) throws Exception {
        while(true) {
            //当前内存有缓冲区的数据
            if (this.currentRecordDeserializer != null) {
                DeserializationResult result;
                try {
                    //从deserializationDelegate尝试获取下一个记录
                    result = this.currentRecordDeserializer.getNextRecord(this.deserializationDelegate);
                } catch (IOException var4) {
                    throw new IOException(String.format("Can't get next record for channel %s", this.lastChannel), var4);
                }
                if (result.isFullRecord()) {
                    //处理该记录并返回
                    this.processElement((StreamElement)this.deserializationDelegate.getInstance(), output);
                    return DataInputStatus.MORE_AVAILABLE;
                }
            }
            //通过pollNext()方法从checkpointedInputGate中获取下一个元素,并将其封装在Optional中。
            Optional<BufferOrEvent> bufferOrEvent = this.checkpointedInputGate.pollNext();
            //然后检查bufferOrEvent是否存在
            if (bufferOrEvent.isPresent()) {
                //如果是缓冲区,则调用processBuffer方法进行处理
                if (((BufferOrEvent)bufferOrEvent.get()).isBuffer()) {
                    this.processBuffer((BufferOrEvent)bufferOrEvent.get());
                    continue;
                }
                //如果是事件,则调用processEvent方法进行处理并返回结果
                return this.processEvent((BufferOrEvent)bufferOrEvent.get());
            }
        }
    }
}

最终调的是父类AbstractStreamTaskNetworkInputemitNext方法

3、从缓冲区获取数据放入到内存中

通过上面emitNext实现,while循环中先判断当前内存区是否有缓冲区的数据,有则处理结束此次emitNext方法,如果没有则从缓冲区获取数据到当前内存区,再跳过本次循环,让下一个循环开始执行处理内存区数据的方法
this.checkpointedInputGate.pollNext()这个就不看了,你就知道从缓冲区返回数据就行了,

看一下processBuffer方法

protected void processBuffer(BufferOrEvent bufferOrEvent) throws IOException {
    //获取缓存管道信息
    this.lastChannel = bufferOrEvent.getChannelInfo();
    Preconditions.checkState(this.lastChannel != null);
    //可以理解为给currentRecordDeserializer初始化,选定类型
    this.currentRecordDeserializer = this.getActiveSerializer(bufferOrEvent.getChannelInfo());
    Preconditions.checkState(this.currentRecordDeserializer != null, "currentRecordDeserializer has already been released");
    //把缓冲区的数据写入到当前内存区
    this.currentRecordDeserializer.setNextBuffer(bufferOrEvent.getBuffer());
}

4、调用算子的processElement方法处理数据,

通过StreamOneInputProcessor初始化知道,入参output实际上是OneInputStreamTask里的私有类StreamTaskNetworkOutput对象

private void processElement(StreamElement recordOrMark, DataOutput<T> output) throws Exception {
    if (recordOrMark.isRecord()) {
        //这里就调用了OneInputStreamTask里的私有类StreamTaskNetworkOutput中的emitRecord方法
        output.emitRecord(recordOrMark.asRecord());
    }
}
private static class StreamTaskNetworkOutput<IN> implements DataOutput<IN> {
    private final Input<IN> operator;
    public void emitRecord(StreamRecord<IN> record) throws Exception {
        //调用的算子的processElement方法
        this.operator.processElement(record);
    }
}

emitRecord方法就会调用算子的processElement方法,之后就可以看基础转换函数和窗口函数文章中,他们是被调用processElement触发的
如果不清楚可以看Flink 1.14.*中flatMap,filter等基本转换函数源码

四、sink的streamTask用的也是OneInputStreamTask

sink可以看成是一个像flatMapfilter、窗口一样的算子,通过OneInputStreamTask触发到sinkFuncitionprocessElement方法,执行流程都是一样的,

不懂的可以看下面两篇文章,比对一下,sink和基本转换、窗口算子触发方式是否一样
Flink 1.14.*中flatMap,filter等基本转换函数源码
Flink 1.14.* 版本kafkaSink源码

五、OneInputStreamTask和SourceStreamTask类关系图

在这里插入图片描述

在这里插入图片描述

比对两个关系图,SourceStreamTask多了SourceFunction接口和streamSource

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

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

相关文章

从0到DevOps(1)-初步了解DevOps和容器

DevOps从提出以来陆续成为行业普遍实践&#xff0c;目前是数字化生产普遍不可或缺的信息底座。本系列文章旨在系统性的阐述与认识DevOps, 了解企业实践里DevOps的实际面貌。 什么是DevOps? DevOps 是一套实践、工具和文化理念&#xff0c;为实现用户不断的软件功能和可用性要…

学会这5个AI变现方法,让你在小红书上轻松赚钱!

大家好&#xff01;最近AI真是大火&#xff0c;尤其是ChatGPT、Midjourney这些AI工具&#xff0c;感觉不搞点AI相关的内容&#xff0c;都跟不上潮流啦&#xff01; 作为一个深耕小红书的内容创作者&#xff0c;我发现AI其实在小红书上有着巨大的变现潜力。 那么&#xff0c;如…

C--四种排序方法的补充

上一篇文章因为时间原因只写了三种&#xff0c;这一篇来补充第四种&#xff0c;第四种的代码更多&#xff0c;所需要理解的也是更多的。 堆排序 想要学会堆排序&#xff0c;你必须了解二叉树的内容。堆排序的排序速度也是非常的快。 这里都已大堆为例 1.向上调整算法&#…

JavaWeb - Spring Boot

Spring 官网​​​​​Spring | Home Spring Boot Spring Boot是一个由Pivotal团队提供的开源框架&#xff0c;旨在简化Spring应用的初始搭建以及开发过程。在Spring Boot项目中&#xff0c;通常会有Controller、Service、Mapper和Entity等层次结构。下面将详细介绍这些层次的…

Mac 安装Hadoop教程

1. 引言 本教程旨在介绍在Mac 电脑上安装Hadoop&#xff0c;便于编程开发人员对大数据技术的熟悉和掌握。 2.前提条件 2.1 安装JDK 想要在你的Mac电脑上安装Hadoop&#xff0c;你必须首先安装JDK。具体安装步骤这里就不详细描述了。你可参考Mac 下载JDK8。 2.2 配置ssh环境…

三分钟讲明白怎么用Fusion360和3D打印做模具

前言 模具&#xff0c;这东西听起来好像很常见&#xff0c;但是听到价格又很高大上&#xff0c;但是现在好消息是你可以在家里用3D打印方式实现一个模具&#xff0c;虽然是一个学习级的简易模具但是符合模具的9成要素 这里我们设计一个可以把热熔胶变成实物的模具 如何实现 1首…

生成密码c++

需求 目前需要实现生成8位密码&#xff0c;密码要求至少包含一位数字&#xff0c;一位大写字母&#xff0c;一位小写字母&#xff0c;一位特殊字符。如果用户第一次使用还没有输入密码&#xff0c;密码则为系统随机生成。 用户输入密码&#xff0c;符合规则则将默认密码覆盖掉…

重生之我们在ES顶端相遇第10 章- 分分分词器的基本使用

文章目录 思维导图0. 前言1. 光速上手1.1 指定分词器1.2 测试分词器 2. 分词流程(重要)2.1 基本介绍2.2 深入如何测试分词器 3. 自定义一个简单的分词器 思维导图 0. 前言 分词器在 ES 搜索使用中非常关键&#xff0c;一个好的分词器能够提高搜索的质量&#xff0c;让用户搜索…

进程间的通信(无名管道)

进程间通信 IPC InterProcess Communication 1.进程间通信方式 1.早期的进程间通信&#xff1a; 无名管道(pipe)、有名管道(fifo)、信号(signal) 2.system V PIC&#xff1a; 共享内存(share memory)、信号灯集(semaphore)、消息队列(message queue) 3.BSD: 套接字(socket) 2.无…

AI壁纸套装,单月变现7000+,手把手教你,别说你还不会

介绍 这种类型的手机壁纸&#xff0c;平板壁纸&#xff0c;电脑壁纸&#xff0c;甚至是手表壁纸&#xff0c;流量都很不错&#xff0c;尤其是深受一些女性的喜欢。 变现能力也不错&#xff0c;而且变现方式也多种多样。 今天就一步一步的教大家如何制作这种壁纸&#xff0c;怕…

本地部署 Flux.1 最强文生图大模型!Comfyui 一键安装

前言 最近&#xff0c;由前 Stability AI员工创立的黑森林实验室推出了开源文生图大模型–FLUX.1横空出世。 FLUX.1在文字生成、复杂指令遵循和人手生成上具备优势。以下是其生成图像示例&#xff0c;可以看到即使是生成大段的文字、多个人物&#xff0c;也没有出现字符、人手…

涉案财物管理系统|涉案财物全流程监测

涉案财物管理系统DW-S405系统基于物联网技术规范涉案财物管理流程&#xff0c;确保涉案财物的安全性、完整性和合法性&#xff1b;可以提高办案效率&#xff0c;减少办案成本&#xff0c;实现资源共享。 DW-S405可以深度整合大平台和物理存储区的整体一致性&#xff0c;实现对…

通信算法之229: 通信系统中的Eb/N0与SNR

通信系统中接收灵敏度是衡量系统可接收的最小信号电平。各个文章书籍中都给了接收灵敏度与SNR的关系。 但是做解调算法的工程师却在乎的是Eb/No&#xff0c;那么两者的关系什么&#xff1f;是不是都可以代表接收性能的好坏&#xff1f; Eb/No 在通信系统中&#xff0c;Eb/No 是…

带娃赚钱两不误,用AI做故事绘本,零成本轻松变现

01 利用Chatgpt生成故事脚本内容 AI Breakthroug 这一步我们可以将收集的爆款故事文案给到GPT进行改写&#xff0c;这里我重点展示如何通过提示词让GPT帮我们生成原创的故事脚本。 *▍**让GPT生成原创故事标题* 一开始不知道写什么主题故事的时候&#xff0c;这里我们可以…

报错:java: 不再支持源选项 5。请使用 8 或更高版本

Date: 2024.08.30 13:52:20 author: lijianzhan 电脑环境&#xff1a;Windows10 开发环境&#xff1a;JDK21 代码工具&#xff1a;IntelliJ IDEA 2024 一、问题 运行脚本控制台报错&#xff1a;java: 不再支持源选项 5。请使用 8 或更高版本。 二、原因 当前JDK版本比较高&…

【MySQL索引】4索引优化

索引优化 1 关联查询优化 左连接LEFT JOIN LEFT JOIN 右边是我们的关键点,一定需要建立索引 .这里是book的card 字段&#xff0c;type建不建索引无所谓。 ALTER TABLE book ADD INDEX Y ( card); #【被驱动表】&#xff0c;可以避免全表扫描 EXPLAIN SELECT SQL_NO_CACHE *…

2.5G网络(通常指2.5G以太网,即2500BASE-X)的网络变压器在设计和应用上有几个关键方面

信号传输和接收&#xff1a; 2.5G网络变压器主要用于以太网设备中&#xff0c;用于将信号从平衡转换为非平衡&#xff0c;或者进行阻抗匹配&#xff0c;确保信号能够在传输线和接收器之间有效地传输和接收。 频率范围&#xff1a; 这些变压器需要支持2.5G以太网的频率范围&…

Java 入门指南:Java 并发编程 —— 两万字详解 进程(Process)与线程(Thread)

线程和进程是操作系统中两个重要的概念&#xff0c;用于实现并发执行和多任务处理。 基础概念 进程 进程&#xff08;Process&#xff09;&#xff1a;进程是计算机中正在运行的程序的实例。它是操作系统分配系统资源的基本单位&#xff0c;包括程序代码、数据、打开的文件、…

Global Illumination_LPV Deep Optimizations

接上回&#xff0c;RSM优化技术介绍后&#xff0c;我们本部分主要看一下&#xff0c;光栅GI三部曲中的LPV&#xff0c;这个算法算是很巧妙了&#xff0c;算法思路基于RSM上拓展到世界空间&#xff0c;可以说很具学习和思考价值&#xff0c;之前也简单实现过Global Illumination…

【 html+css 绚丽Loading 】000028 九宫幻明轮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…