BIO、NIO、AIO、Netty从简单理解到使用
Java编程中BIO、NIO、AIO是三种不同的I/O(输入/输出)模型,它们代表了不同的I/O处理方式。
Netty就是基于Java的NIO(New Input/Output)类库编写的一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
先来了解一下基本的三种基本的io模型:
BIO(Blocking I/O,阻塞I/O)
定义:BIO是Java最传统的I/O模型,基于流的同步阻塞I/O操作。每个连接都会占用一个线程,当进行I/O操作时,线程会被阻塞,直到操作完成。
特点:
同步阻塞:每个I/O操作都会阻塞当前线程,直到操作完成。
线程占用:每个连接需要一个独立的线程,线程资源消耗较大。
实现简单:代码实现相对简单,易于理解和编写。
作用:适用于连接数较少且固定的架构,如传统的C/S架构。由于其实现简单、编程直观,因此在一些简单的网络编程场景中仍然被使用。
创建客户端和服务端演示数据接收与传输。
//服务端
public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(9999)) {while (true) {Socket socket = serverSocket.accept(); // 持续监听新连接new Thread(() -> { // 为每个客户端创建独立线程try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {System.out.println("客户端[" + socket.getPort() + "] 消息: " + line);}} catch (IOException e) {e.printStackTrace();}}).start();}} catch (IOException e) {e.printStackTrace();}}//客户端
public static void main(String[] args) {System.out.println("客户端启动...");try {//创建套接字Socket socket = new Socket("127.0.0.1", 9999);//获取输出流发送消息PrintStream ps = new PrintStream(socket.getOutputStream());Scanner sc = new Scanner(System.in);while (true){System.out.println("请输入信息:");ps.println(sc.nextLine());ps.flush();}} catch (IOException e) {throw new RuntimeException(e);}}
NIO(Non-blocking I/O,非阻塞I/O)
定义:NIO是Java 1.4引入的新I/O API,基于通道(Channel)和缓冲区(Buffer)的非阻塞I/O操作。
特点:
非阻塞:I/O操作不会阻塞线程,线程可以在等待数据时执行其他任务。
多路复用:通过Selector可以管理多个Channel,提高了资源利用率。
高性能:适用于高并发场景,减少线程开销和上下文切换。
核心组件:
缓冲区(Buffer):用于存储数据的固定大小的内存区域,提供了多种类型的缓冲区,如ByteBuffer、CharBuffer等。
通道(Channel):用于数据读写的通道,支持非阻塞模式,与缓冲区配合使用。
选择器(Selector):允许单个线程同时处理多个通道的I/O事件。
作用:适用于连接数较多且连接较短的架构,如高并发的服务器端应用。NIO提供了更高效、更灵活的I/O操作方式,显著提高了系统的并发性能和吞吐量。
//服务端public static void main(String[] args) throws Exception {//创建连接通道,ServerSocketChannel 绑定端口,监听新的客户端连接请求。它本身不处理任何数据传输。ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 设置通道为非阻塞模式serverSocketChannel.configureBlocking(false);//绑定端口serverSocketChannel.bind(new InetSocketAddress(9999));//创建 Selector 并注册 ACCEPT 事件Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("NIO 服务端启动...");//阻塞等待事件发生while (selector.select()>0){//获取到所有事件Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//遍历所有事件while (iterator.hasNext()){SelectionKey key = iterator.next();//检查是否有新的连接请求if(key.isAcceptable()){//为每一个创建新的通道SocketChannel clientChannel = serverSocketChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector,SelectionKey.OP_READ);}else if(key.isReadable()){//处理读数据SocketChannel channel = (SocketChannel)key.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);int bytesRead = 0;while ((bytesRead = channel.read(byteBuffer)) > 0){byteBuffer.flip();String message = new String(byteBuffer.array(),0,bytesRead);System.out.println("接收客户端信息:"+message);byteBuffer.clear();//回写数据给客户端ByteBuffer response = ByteBuffer.wrap(("ECHO: " + message).getBytes());channel.write(response);}}iterator.remove();}}}//客户端
public static void main(String[] args) throws Exception {// 1. 创建 SocketChannel 并连接服务器SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));socketChannel.configureBlocking(false); // 非阻塞模式System.out.println("NIO 客户端已连接");//接收线程new Thread(() -> {ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) {try {int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("收到服务端响应: " + new String(data));buffer.clear();}} catch (IOException e) {System.out.println("连接已断开");break;}}}).start();//发送消息ByteBuffer byteBuffer = ByteBuffer.allocate(1024);Scanner sc = new Scanner(System.in);while (true){System.out.println("请输入信息:");String msg = sc.nextLine();byteBuffer.put(("AA:"+msg).getBytes(StandardCharsets.UTF_8));byteBuffer.flip();socketChannel.write(byteBuffer);byteBuffer.clear();}}
AIO(Asynchronous I/O,异步I/O)
定义:AIO是Java 7引入的异步I/O API,基于异步通道(AsynchronousChannel)和异步回调机制。
特点:
异步非阻塞:I/O操作是异步的,通过回调处理结果,不阻塞线程。
回调机制:通过回调函数处理I/O操作结果,提高了程序的灵活性。
高性能:适用于高并发、高吞吐量的场景。
工作方式:
程序发起一个异步I/O请求,并提供一个回调函数。
程序无需等待I/O操作完成,而是立即返回,继续执行其他任务。
当I/O操作完成后,系统会调用之前提供的回调函数,传递结果或状态信息。
作用:适用于连接数较多且连接时间较长的架构,如高并发的服务器端应用。AIO提供了真正的异步I/O操作方式,进一步提高了系统的并发性能和吞吐量。
Netty
先了解一下netty出现的原因,是干嘛用的?
- Netty出现的原因:
Java NIO的复杂性:
Java NIO虽然提供了非阻塞IO的能力,但其API设计较为底层,使用起来比较复杂。开发者需要处理大量的细节,如选择器的管理、缓冲区的操作等,这增加了开发的难度和出错的风险。
性能瓶颈:
在高并发场景下,直接使用Java NIO进行网络编程可能会遇到性能瓶颈。例如,选择器的实现和多线程管理的复杂性可能导致性能下降。
缺乏高级功能:
Java NIO仅提供了基础的非阻塞IO机制,缺乏一些高级功能,如协议编解码、连接管理等。这些功能在开发网络应用时非常重要,但实现起来却相对复杂。
- Netty的具体用途是什么?
简化网络编程:
Netty封装了Java NIO的复杂性,提供了一套简洁易用的API。开发者可以使用这些API快速构建网络应用,而无需关注底层的细节。
提高性能:
Netty采用了异步非阻塞IO模型,并支持零拷贝等技术,可以在保证高性能的同时,减少CPU和内存资源的消耗。这使得Netty在高并发场景下表现尤为出色。
支持多种协议:
Netty内置了对多种协议的支持,如TCP、UDP、HTTP、WebSocket等。开发者可以轻松地使用这些协议构建网络应用,而无需自己实现协议编解码等复杂功能。
灵活的扩展性:
Netty提供了丰富的扩展点,如ChannelHandler、Codec等。开发者可以通过实现这些接口来扩展Netty的功能,以满足特定的业务需求。
广泛的应用场景:
Netty经过广泛的使用和验证,具有高稳定性和可靠性,适用于各种网络应用场景,如分布式系统、微服务架构中的通信组件、实时通讯系统、游戏服务器等。
它提供了丰富的错误处理和恢复机制,能够有效地处理网络通信中的各种异常情况。
netty核心优势:
异步事件驱动、零拷贝、内存池、高度可定制。
- 核心组件
先大概了解一下 Reactor 模型(单线程、多线程、主从多线程)。
因为Reactor 模式是 Netty 高性能和高并发能力的核心设计基础。Netty 的线程模型、事件驱动机制和异步非阻塞 I/O 都深度依赖 Reactor 模式。
Reactor 模式是一种 事件驱动的设计模式,用于处理高并发的 I/O 请求。其核心思想是 用一个或多个线程监听事件(如连接、读写请求),并将事件分发给对应的处理器异步处理,避免线程阻塞和资源浪费。
Reactor模型的核心组件
事件源(Event Source):指任何可以产生I/O事件的对象,例如网络连接、文件、设备等。
事件循环(Event Loop):Reactor模型的核心,负责监控所有事件源的状态,并在有事件发生时将其分发给相应的处理程序(即回调函数)。
事件处理器(Event Handler):处理特定事件的代码模块,通常实现为回调函数或方法。针对特定的事件源,每一个事件源通常都有一个相应的处理器,用于处理该事件源的I/O事件。
多路复用器(Demultiplexer):用于监视多个事件源并将发生的事件通知给Reactor。常见的实现包括Java的Selector、Linux的epoll等。
Reactor模型的实现方式
单Reactor单线程模型:所有操作(连接、读写)由一个线程完成。设计简单,但在高并发场景下容易成为性能瓶颈。
单Reactor多线程模型:Reactor线程负责监听和分发事件,而事件的处理则交给线程池中的工作线程完成。这种方式能够充分利用多核CPU的处理能力,但在高并发场景下,Reactor线程可能成为性能瓶颈。
主从Reactor多线程模型(Netty 默认模型-高并发):也称为多Reactor多线程模型。主Reactor线程负责监听和分发连接事件,当有新的连接到来时,将其分发给从Reactor线程处理。从Reactor线程负责监听和分发读写事件,并交给线程池中的工作线程处理。这种方式能够进一步提高系统的并发处理能力和可扩展性。
写个netty服务端示例看一下:
// 1. 创建主 Reactor(处理连接)管理一组 EventLoop,每个线程绑定一个 EventLoop(事件循环线程)
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 主线程组(通常 1 个线程)
// 2. 创建子 Reactor(处理 I/O)
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 子线程组(默认 CPU 核数 × 2)//Reactor 模式配置入口 绑定主/子线程组、设置 Channel 类型
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup, workerGroup) // 绑定主从线程组.channel(NioServerSocketChannel.class) // 设置 Channel 类型(NIO)代表一个 Socket 连接或监听端口.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new ServerHandler()); // 添加业务处理器,这里需要自己实现}});// 3. 绑定端口并启动服务
ChannelFuture future = server.bind(8080).sync();
//等待服务器通道关闭
future.channel().closeFuture().sync();
//关闭通道
finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}//业务处理器
public class ServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("客户端连接成功...");}}
- 组件说明:
Channel:
在Netty中,Channel是一个核心概念,Channel提供了数据的读取和写入操作,以及与远程端点进行通信的能力。
通过Channel可以获取到很多信息,比如:本地地址、远程地址、Channel的EventLoop、Channel的Pipeline等。
Channel 生命周期:
channelRegistered (初始化操作)→ channelActive (连接)→ channelRead(数据接收) → channelInactive(断开)→ channelUnregistered(注销)
Selector:
Selector是Java NIO(New Input/Output)中的一个核心组件,用于监控多个Channel(通道)的状态,例如连接、读、写等事件。在netty中Selector被封装在NioEventLoop中,当Channel注册到Selector并指定感兴趣的事件类型(如连接、读、写等)后,会返回一个SelectionKey,用于表示Selector与Channel之间的关联关系。Selector会不断地轮询其注册的Channel,如果有事件发生,Selector会将相应的SelectionKey放入就绪事件集合中。事件循环线程会从就绪事件集合中取出SelectionKey,并根据事件类型调用相应的ChannelHandler进行处理。处理完成后,事件循环线程会将Channel放回到Selector中,继续等待下一次事件的发生。
EventLoopGroup:
管理一组 EventLoop,通常分为 bossGroup(处理连接)和 workerGroup(处理 I/O)。
配置线程数:new NioEventLoopGroup(4) 表示 4 个线程。
ServerBootstrap:
用于启动服务器端的引导类,配置服务器参数和处理器链配置、启动服务器、关闭服务器。
- 配置服务器参数:
线程模型:ServerBootstrap允许您配置两个主要的线程组:bossGroup和workerGroup。
bossGroup用于处理客户端的连接请求,而workerGroup用于处理已连接的客户端的I/O操作。
通道类型:通过channel方法,您可以指定服务器使用的通道类型。对于NIO传输,通常使用NioServerSocketChannel。
通道选项:使用option方法可以设置服务器通道的选项,如SO_BACKLOG(TCP连接请求的最大队列长度)和SO_REUSEADDR(允许地址重用)。
子通道选项:childOption方法用于设置已连接客户端的通道选项,如SO_KEEPALIVE(保持连接活动状态)。
Option:可以应用于Channel或Bootstrap上。
ChannelOption:专门用于配置Channel的参数。
- 处理器链配置:
父处理器:通过handler方法,您可以设置处理服务器通道I/O事件的处理器。这通常用于处理连接请求。
子处理器:childHandler方法用于设置处理已连接客户端I/O事件的处理器。这是开发者编写业务逻辑处理代码的地方,通常通过添加一系列的ChannelHandler来实现。
ChannelHandler
ChannelHandler是ChannelPipeline中的基本处理单元,负责处理或拦截入站和出站事件。每个ChannelHandler都有一个关联的ChannelHandlerContext,通过它可以方便地与其他组件进行交互。ChannelPipeline则是ChannelHandler的容器,负责ChannelHandler的管理和事件拦截。
pipeline、ChannelPipeline
Pipeline是ChannelPipeline的简称。ChannelPipeline用于处理网络事件和数据的流动。负责将一系列的处理逻辑(称为ChannelHandler)串联起来,形成一个处理链,对网络事件进行拦截和处理。
事件处理:ChannelPipeline负责处理入站(Inbound)和出站(Outbound)事件,如数据读取、数据写入、连接建立、连接断开等。
拦截器链:ChannelPipeline内部维护了一个拦截器链(或称为处理器链),每个拦截器(即ChannelHandler)都可以对事件进行处理或拦截。
动态性:ChannelPipeline允许在运行时动态地添加、删除或替换拦截器,从而灵活地扩展和定制网络处理逻辑。
-
启动服务器:
配置完成后,通过调用bind方法并传入服务器的端口号,ServerBootstrap将启动服务器并绑定到指定的端口。
bind方法返回一个ChannelFuture对象,您可以使用sync方法等待服务器启动完成。
ChannelFuture是Netty框架中特有的接口,继承自Java的Future接口。在Netty中,所有的I/O操作都是异步的,ChannelFuture用于在操作完成时通知应用程序,以便应用程序可以执行某些操作或检索操作的结果。 -
关闭:
当服务器需要关闭时,可以调用ChannelFuture对象的channel().closeFuture().sync()方法,等待服务器通道关闭。
最后,调用bossGroup和workerGroup的shutdownGracefully方法,以优雅地断开连接并关闭线程组。
ChannelHandlerContext:
ChannelHandlerContext封装了Channel和ChannelPipeline,使得ChannelHandler可以方便地访问和操作ChannelPipeline和Channel。
ChannelHandlerContext提供了许多方法,用于操作和传播事件,如写入数据、刷新数据、触发读事件等。
ChannelHandlerContext允许ChannelHandler将事件传递给ChannelPipeline中的下一个处理器。例如,当一个入站事件(如数据读取)发生时,ChannelHandlerContext可以调用fireChannelRead方法,将事件传递给下一个入站处理器。
相关文章:
BIO、NIO、AIO、Netty从简单理解到使用
Java编程中BIO、NIO、AIO是三种不同的I/O(输入/输出)模型,它们代表了不同的I/O处理方式。 Netty就是基于Java的NIO(New Input/Output)类库编写的一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可…...
计算机毕业设计SpringBoot+Vue.js工厂车间管理系统源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
一、图形图像的基本概念
文章目录 一、分辨率概念二、图形图像的区别三、位图和矢量图的区别 一、分辨率概念 图形显示计数中的分辨率概念有三种,即屏幕分辨率、显示分辨率和显卡分辨率。它们既有区别又有着密切的联系,对图形显示的处理有极大的影响。 1.屏幕分辨率 显示器分辨…...
前端跨域问题初探:理解跨域及其解决方案概览
在当今的Web开发中,跨域问题是一个常见且棘手的挑战 随着前端技术的不断进步,越来越多的应用需要从不同的域名、协议或端口获取资源 然而,浏览器的同源策略(Same-Origin Policy)限制了这种跨域请求,以确保…...
SQL分组问题
下列为电商公司用户访问时间数据 统计某个用户连续的访问记录,如果时间间隔小于60s,就分为一组 id ts 1001 17523641234 1001 17523641256 1002 17523641278 1001 17523641334 1002 17523641434 1001 17523641534 1001 17523641544 1002 17523…...
Oracle 数据库基础入门(二):深入理解表的约束
在 Oracle 数据库的学习进程中,表的约束是构建健壮、准确且高效数据库的关键要素。约束如同数据库的 “规则守护者”,它通过对数据的限制,确保了数据的完整性和一致性,就如同交通规则保障道路上车辆行驶的有序性一样。对于 Java 全…...
DeepSeek掘金——DeepSeek-R1驱动的房地产AI代理
DeepSeek掘金——DeepSeek-R1驱动的房地产AI代理 本文探讨如何使用 Smolagents 和 DeepSeek-R1 构建房地产代理,并利用工具进行网页抓取和数据导出。 AI 代理对于自动化复杂的推理任务至关重要。Smolagents 是由 Hugging Face 开发的轻量级 AI 代理框架,允许将大型语言模型 …...
WebP2P技术在嵌入式设备中的应用:EasyRTC音视频通话SDK如何实现高效通信?
在数字化时代,实时通信技术(RTC)与人工智能(AI)的融合正在重塑各个行业的交互方式。从在线教育到远程医疗,从社交娱乐到企业协作,RTC的应用场景不断拓展。然而,传统的RTC解决方案往往…...
【零基础到精通Java合集】第三集:流程控制与数组
针对**“流程控制与数组”**的15分钟课程内容设计,聚焦代码逻辑设计与数据结构基础: 课程标题:流程控制与数组(15分钟) 目标:掌握分支/循环语法与数组操作,能编写条件判断与数据遍历程序 一、课程内容与时间分配 0-2分钟 课程目标与逻辑导图 明确学习目标:分支结构、…...
VsCode + EIDE + OpenOCD + STM32(野火DAP) 开发环境配置
VsCode EIDE OpenOCD STM32(野火DAP) 开发环境配置 接受了新时代编辑器的我,实在受不了Keil的上古编辑页面,周树人说过:由奢入俭难,下面我们一起折腾一下开源软件Vscode, 用以开发51和STM32,有错误之处&…...
【vue-echarts】——01.认识echarts
文章目录 前言一、echarts二、使用步骤1.vue cli创建项目并安装第三方模块echarts2.显示图表总结前言 定制的数据可视化图表。ECharts最初由百度团队开源,并于2018年初捐赠给Apache基金会,成为ASF孵化级项目。2021年1月26日晚,Apache基金会官方宣布ECharts项目正式毕业。 一…...
【http://noi.openjudge.cn/】4.3算法之图论——1538:Gopher II
[【http://noi.openjudge.cn/】4.3算法之图论——1538:Gopher II] 题目 查看提交统计提问 总时间限制: 2000ms 内存限制: 65536kB 描述 The gopher family, having averted the canine threat, must face a new predator. The are n gophers and m gopher holes, each at di…...
Linux常见操作命令
Linux系统拥有丰富的命令行工具,通过这些命令可以高效地完成各种系统管理和日常操作任务。以下是一些常见的Linux操作命令: 文件和目录操作: - 创建目录:使用 mkdir 命令,例如 mkdir test 可以创建名为 test 的目录。如…...
Linux下测试Wifi性能——2.Linux下wifi指令
一、前言 相关知识大家看前一章节 Linux下测试Wifi性能——1.Wifi相关知识-CSDN博客 二、指令 1.查找可用网卡 iw dev 其中 接口名称(Interface) p2p0 和 wlan0 都是无线接口(网卡)的名称。 wlan0 是常见的无线局域网接口名…...
(十 九)趣学设计模式 之 中介者模式!
目录 一、 啥是中介者模式?二、 为什么要用中介者模式?三、 中介者模式的实现方式四、 中介者模式的优缺点五、 中介者模式的应用场景六、 总结 🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,…...
Leetcode 54: 螺旋矩阵
Leetcode 54: 螺旋矩阵 是一道经典的矩阵遍历模拟题目,要求我们以螺旋顺序遍历一个二维数组。这个问题在面试中非常经典,考察模拟、数组操作以及逻辑清晰度。掌握本题的高效解法可以迅速给面试官留下好印象。 适合面试的解法:边界法ÿ…...
abseil-cpp:环境搭建
参考: https://abseil.io/docs/cpp/quickstart-cmake abseil-cpp.git/dd4c89b abseil-cpp.git/20240722.1 1. clone代码仓库、编译 git clone https://github.com/abseil/abseil-cpp.git /app/abseil-cpp/ #/app/abseil-cpp/.git/config git checkout 20240722.1git rev-pa…...
Centos7部署k8s(单master节点安装)
单master节点部署k8s集群(Centos) 一、安装前准备 1、修改主机名 按照资源准备修改即可 # master01 hostnamectl set-hostname master01 ; bash # node1 hostnamectl set-hostname node1 ; bash # node2 hostnamectl set-hostname node2 ; bash2、修改hosts文件 以下命令所…...
RPA 职业前景:个人职场发展的 “新机遇”
1. RPA职业定义与范畴 1.1 RPA核心概念 机器人流程自动化(RPA)是一种通过软件机器人模拟人类操作,自动执行重复性、规则性任务的技术。RPA的核心在于其能够高效、准确地处理大量数据和流程,减少人工干预,从而提高工作…...
详解DeepSeek模型底层原理及和ChatGPT区别点
一、DeepSeek大模型原理 架构基础 DeepSeek基于Transformer架构,Transformer架构主要由编码器和解码器组成,在自然语言处理任务中,通常使用的是Transformer的解码器部分。它的核心是自注意力机制(Self - Attention),这个机制允许模型在处理输入序列时,关注序列中不同位…...
Comsol 锂枝晶模型 “五合一”:探索枝晶生长的多元奥秘
comsol 锂枝晶模型 五合一 单枝晶定向生长、多枝晶定向生长、多枝晶 随机生长只 无序生长随机形核以及雪花枝晶,包含相场、浓度场和电场三种物理场在锂电领域,锂枝晶的生长一直是研究的重点,因为它严重影响电池的安全性与性能。今天咱就来唠唠…...
EasyAnimateV5中文模型快速部署:Docker Compose一键拉起全栈服务
EasyAnimateV5中文模型快速部署:Docker Compose一键拉起全栈服务 1. 开篇:让图片动起来的AI魔法 你有没有想过,一张静态的照片能在几秒钟内变成生动的视频?现在,这个想法已经变成了现实。EasyAnimateV5中文模型就是这…...
从多项式逼近到优化求解:泰勒展开与拉格朗日乘子的机器学习实践
1. 泰勒展开:机器学习的"局部望远镜" 第一次接触泰勒公式时,我的数学老师用了个有趣的比喻:这就像用乐高积木拼凑复杂雕塑的局部轮廓。在机器学习中,这个思想被广泛应用——当我们面对复杂的损失函数曲面时,…...
Codex CLI 多环境配置秘籍:如何用 profiles 一键切换 OpenAI/Mistral/Ollama
Codex CLI 多环境配置秘籍:如何用 profiles 一键切换 OpenAI/Mistral/Ollama 当你的开发工作流需要同时对接多个AI模型提供商时——比如公司项目使用OpenAI的GPT-4,个人实验采用本地Ollama托管的Mistral,而临时调试又需要连接Azure的API端点—…...
影刀RPA与Python变量管理:全局与局部变量的实战应用
1. 全局变量与局部变量的核心区别 在影刀RPA中编写Python脚本时,变量管理是影响代码质量的关键因素。全局变量就像办公室的公告板,所有部门(函数)都能看到并修改;而局部变量则是员工个人笔记本上的临时记录,…...
LabelImg图像标注工具:3分钟掌握高效目标检测数据标注技巧
LabelImg图像标注工具:3分钟掌握高效目标检测数据标注技巧 【免费下载链接】labelImg LabelImg is now part of the Label Studio community. The popular image annotation tool created by Tzutalin is no longer actively being developed, but you can check ou…...
如何快速上手VNote:跨平台Markdown笔记软件的完整指南
如何快速上手VNote:跨平台Markdown笔记软件的完整指南 【免费下载链接】vnote A pleasant note-taking platform. 项目地址: https://gitcode.com/gh_mirrors/vn/vnote VNote是一款基于Qt开发的免费开源Markdown笔记应用,专为追求高效编辑体验的用…...
终极指南:3分钟掌握原神圣遗物扫描工具Amenoma的完整使用技巧 [特殊字符]
终极指南:3分钟掌握原神圣遗物扫描工具Amenoma的完整使用技巧 🎯 【免费下载链接】Amenoma A simple desktop application to scan and export Genshin Impact Artifacts and Materials. 项目地址: https://gitcode.com/gh_mirrors/am/Amenoma 还…...
RxDataSources编辑功能详解:如何实现TableView的增删改操作
RxDataSources编辑功能详解:如何实现TableView的增删改操作 【免费下载链接】RxDataSources UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...) 项目地址: https://gitcode.com/gh_mirrors/rx/RxDataSources…...
OpenClaw内容创作流水线:nanobot镜像从选题到发布的自动化
OpenClaw内容创作流水线:nanobot镜像从选题到发布的自动化 1. 为什么需要内容创作自动化 作为一名技术博主,我每天都要面对一个永恒难题:如何在有限时间内持续产出高质量内容。传统写作流程需要经历选题调研、大纲设计、初稿撰写、SEO优化、…...
