当前位置: 首页 > news >正文

rust学习-构建服务器

单线程server

服务器会依次处理每一个请求,在完成第一个连接的处理之前不会处理第二个连接

// cat main.rs
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {// stream 此时类型:core::result::Result<std::net::tcp::TcpStream, std::io::error::Error>// unwrap() 要么取出Result的Some中的值,要么Paniclet stream = stream.unwrap();// stream 此时类型:std::net::tcp::TcpStreamhandle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024]; // [dataType;size],为每一个元素初始化为0stream.read(&mut buffer).unwrap();// 浏览器输入得到内容如下// Request: GET /favicon.ico HTTP/1.1// Host: 127.0.0.1:7878// Connection: keep-alive// sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"// sec-ch-ua-mobile: ?0// User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36// sec-ch-ua-platform: "macOS"// Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8// Sec-Fetch-Site: same-origin// Sec-Fetch-Mode: no-cors// Sec-Fetch-Dest: image// Referer: http://127.0.0.1:7878/// Accept-Encoding: gzip, deflate, br// Accept-Language: zh-CN,zh;q=0.9println!("Request: {}", String::from_utf8_lossy(&buffer[..]));// 一个微型的成功 HTTP 响应,格式如下// HTTP-Version Status-Code Reason-Phrase CRLF// headers CRLF// message-body// let response = "HTTP/1.1 200 OK\r\n\r\n";// 给前端传输htmllet contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",contents.len(),contents);// flush 会等待并阻塞程序执行直到所有字节都被写入连接中// TcpStream 包含一个内部缓冲区来最小化对底层操作系统的调用stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}// 客户端的请求行
// Method Request-URI HTTP-Version CRLF
// headers CRLF
// message-body
// hello.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>Hello!</title></head><body><h1>Hello!</h1><p>Hi from Rust</p></body>
</html>

区分响应

// cat main.rs
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));// 将与 / 请求相关的数据硬编码进变量 get// 数据开头增加 b"" 字节字符串语法将其转换为字节字符串let get = b"GET / HTTP/1.1\r\n";// 检查 buffer 是否以 get 中的字节开头。如果是,这就是一个格式良好的 / 请求if buffer.starts_with(get) {let contents = fs::read_to_string("hello.html").unwrap();let response = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();} else {// 如果 buffer 不 以 get 中的字节开头,返回404let status_line = "HTTP/1.1 404 NOT FOUND";let contents = fs::read_to_string("404.html").unwrap();let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();}
}

404.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>Hello!</title></head><body><h1>Oops!</h1><p>Sorry, I don't know what you're asking for.</p></body>

重构 handle_connection

fn handle_connection(mut stream: TcpStream) {// --snip--let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK", "hello.html")} else {("HTTP/1.1 404 NOT FOUND", "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}

单线程

use std::thread;
use std::time::Duration;
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();handle_connection(stream);}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));let get = b"GET / HTTP/1.1\r\n";let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 OK", "hello.html")} else {("HTTP/1.1 404 NOT FOUND", "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}

多线程

奔溃版本1

fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();for stream in listener.incoming() {let stream = stream.unwrap();thread::spawn(|| {handle_connection(stream);});}
}
//  thread::spawn的类型
// F是参数类型,T是返回类型
// F 的 trait 有 (FnOnce() -> T)、Send,生命周期有  'static
// 处理请求的线程只会执行闭包一次,所以用FnOnce
// 需要 Send 来将闭包从一个线程转移到另一个线程
// 'static 是因为并不知道线程会执行多久
pub fn spawn<F, T>(f: F) -> JoinHandle<T>whereF: FnOnce() -> T + Send + 'static,T: Send + 'static

根据奔溃版本和thread::spawn构造假想线程池

fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();let pool = ThreadPool::new(4);for stream in listener.incoming() {let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}
}

impl ThreadPool {// --snip--pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static{}
}

构造线程池-糟糕版本

thread::spawn,期望获取一些一旦创建线程就应该执行的代码
但是对于线程池不适用,线程池是当在需要时才执行代码执行

(1)定义 Worker 结构体存放 id 和 JoinHandle<()>
(2)修改 ThreadPool 存放一个 Worker 实例的 vector
(3)定义 Worker::new 函数,它获取一个 id 数字并返回一个带有 id 和用空闭包分配的线程的 Worker 实例
(4)在 ThreadPool::new 中,使用 for 循环计数生成 id,使用这个 id 新建 Worker,并储存进 vector 中

execute 将通过 ThreadPool ,向其中空闲的线程 Worker 实例发送任务。
(1)ThreadPool 会创建一个通道并充当发送端。
(2)每个 Worker 将会充当通道的接收端。
(3)新建一个 Job 结构体来存放用于向通道中发送的闭包。
(4)execute 方法会在通道发送端发出期望执行的任务。
(5)在线程中,Worker 会遍历通道的接收端并执行任何接收到的任务。

use std::thread;
use std::sync::mpsc;pub struct ThreadPool {workers: Vec<Worker>,// threads: Vec<thread::JoinHandle<()>>,sender: mpsc::Sender<Job>,
}struct Job;impl ThreadPool {/// 创建线程池。////// 线程池中线程的数量。////// # Panics////// `new` 函数在 size 为 0 时会 panic。pub fn new(size: usize) -> ThreadPool {// 创建一个没有任何线程的线程池应该是不可恢复的错误assert!(size > 0);/*// with_capacity, 与 Vec::new 做了同样的工作// 它为 vector 预先分配空间// 预分配比 Vec::new 要稍微有效率let mut threads = Vec::with_capacity(size);for _ in 0..size {// create some threads and store them in the vector}ThreadPool {threads}*//*let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers}*/// 创建通道并让 ThreadPool 实例充当发送端// Job是通道的待执行任务let (sender, receiver) = mpsc::channel();let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id, receiver));}ThreadPool {workers,sender,}}// 在 execute 方法中获得期望执行的闭包pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static{}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}/*
impl Worker {fn new(id: usize) -> Worker {// 一个空闭包let thread = thread::spawn(|| {});Worker {id,thread,}}
}
*/impl Worker {// Rust 所提供的通道实现是多生产者,单消费者// 将通道的接收端传递给 workerfn new(id: usize, receiver: mpsc::Receiver<Job>) -> Worker {let thread = thread::spawn(|| {receiver;});Worker {id,thread,}}
}

终极版本

// cat lib.rs
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;pub struct ThreadPool {workers: Vec<Worker>,// threads: Vec<thread::JoinHandle<()>>,sender: mpsc::Sender<Job>,
}type Job = Box<dyn FnOnce() + Send + 'static>;impl ThreadPool {/// 创建线程池。////// 线程池中线程的数量。////// # Panics////// `new` 函数在 size 为 0 时会 panic。pub fn new(size: usize) -> ThreadPool {// 创建一个没有任何线程的线程池应该是不可恢复的错误assert!(size > 0);/*// with_capacity, 与 Vec::new 做了同样的工作// 它为 vector 预先分配空间// 预分配比 Vec::new 要稍微有效率let mut threads = Vec::with_capacity(size);for _ in 0..size {// create some threads and store them in the vector}ThreadPool {threads}*//*let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers}*/// 创建通道并让 ThreadPool 实例充当发送端// Job是通道的待执行任务/*let (sender, receiver) = mpsc::channel();let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id));}ThreadPool {workers,sender,}*/let (sender, receiver) = mpsc::channel();let receiver = Arc::new(Mutex::new(receiver));let mut workers = Vec::with_capacity(size);for id in 0..size {workers.push(Worker::new(id, Arc::clone(&receiver)));}ThreadPool {workers,sender,}}// 在 execute 方法中获得期望执行的闭包pub fn execute<F>(&self, f: F)whereF: FnOnce() + Send + 'static{let job = Box::new(f);self.sender.send(job).unwrap();}
}struct Worker {id: usize,thread: thread::JoinHandle<()>,
}/*
impl Worker {fn new(id: usize) -> Worker {// 一个空闭包let thread = thread::spawn(|| {});Worker {id,thread,}}
}
*/// Rust 所提供的通道实现是多生产者,单消费者
// 将通道的接收端传递给 worker
impl Worker {fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {let thread = thread::spawn(move || {loop {let job = receiver.lock().unwrap().recv().unwrap();println!("Worker {} got a job; executing.", id);job();}});Worker {id,thread,}}
}
// cat main.rs
use std::thread;
use std::time::Duration;
use std::fs;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use rust_demo::ThreadPool;fn main() {let listener = TcpListener::bind("127.0.0.1:7878").unwrap();let pool = ThreadPool::new(4);for stream in listener.incoming() {let stream = stream.unwrap();pool.execute(|| {handle_connection(stream);});}
}fn handle_connection(mut stream: TcpStream) {let mut buffer = [0; 1024];stream.read(&mut buffer).unwrap();println!("Request: {}", String::from_utf8_lossy(&buffer[..]));let get = b"GET / HTTP/1.1\r\n";let sleep = b"GET /sleep HTTP/1.1\r\n";let (status_line, filename) = if buffer.starts_with(get) {("HTTP/1.1 200 OK", "hello.html")} else if buffer.starts_with(sleep) {thread::sleep(Duration::from_secs(5));("HTTP/1.1 200 OK", "hello.html")} else {("HTTP/1.1 404 NOT FOUND", "404.html")};let contents = fs::read_to_string(filename).unwrap();let response = format!("{}\r\nContent-Length: {}\r\n\r\n{}",status_line,contents.len(),contents);stream.write(response.as_bytes()).unwrap();stream.flush().unwrap();
}

相关文章:

rust学习-构建服务器

单线程server 服务器会依次处理每一个请求&#xff0c;在完成第一个连接的处理之前不会处理第二个连接 // cat main.rs use std::io::prelude::*; use std::net::TcpListener; use std::net::TcpStream;fn main() {let listener TcpListener::bind("127.0.0.1:7878&quo…...

Java并发----进程、线程、并行、并发

一、进程与线程 进程 程序由指令和数据组成&#xff0c;但这些指令要运行&#xff0c;数据要读写&#xff0c;就必须将指令加载至 CPU&#xff0c;数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的 当一个程序被运行…...

【计算机网络】第 4 课 - 物理层

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、物理层的基本概念 2、物理层协议的主要任务 3、物理层任务 4、总结 1、物理层的基本概念 在计算机网络中&#xff0c;用来…...

深入理解MVVM架构模式

MVVM原理 MVVM是一种用于构建用户界面的软件架构模式&#xff0c;它的名称代表着三个组成部分&#xff1a;Model&#xff08;模型&#xff09;、View&#xff08;视图&#xff09;和ViewModel&#xff08;视图模型&#xff09;。MVVM的主要目标是将应用程序的UI与其底层数据模…...

配置IPv6 over IPv4手动隧道示例

组网需求 如图1所示&#xff0c;两台IPv6主机分别通过SwitchA和SwitchC与IPv4骨干网络连接&#xff0c;客户希望两台IPv6主机能通过IPv4骨干网互通。 图1 配置IPv6 over IPv4手动隧道组网图 配置思路 配置IPv6 over IPv4手动隧道的思路如下&#xff1a; 配置IPv4网络。配置接…...

Vue3--->组合式API与Pinia

目录 使用create-vue搭建 1、使用create-vue创建项目 2、项目目录和关键文件 组合式API 1、组合式API - setup选项 2、组合式API - reactive和ref函数 3、组合式API - computed 4、组合式API - watch 1、基础使用 - 侦听单个数据 2、基础使用 - 侦听多个数据 3、immediate&…...

三言两语说透柯里化和反柯里化

JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是两种很有用的技术&#xff0c;可以帮助我们写出更加优雅、泛用的函数。本文将首先介绍柯里化的概念、实现原理和应用场景&#xff0c;然后介绍反柯里化的概念、实现原理和应用场景&#xff0c;通过大量的代码示例帮助读…...

细讲TCP三次握手四次挥手(四)

常见面试题 为什么TCP连接的时候是3次&#xff1f;2次不可以吗&#xff1f; 因为需要考虑连接时丢包的问题&#xff0c;如果只握手2次&#xff0c;第二次握手时如果服务端发给客户端的确认报文段丢失&#xff0c;此时服务端已经准备好了收发数(可以理解服务端已经连接成功)据…...

HarmonyOS/OpenHarmony元服务开发-配置卡片的配置文件

卡片相关的配置文件主要包含FormExtensionAbility的配置和卡片的配置两部分&#xff1a; 1.卡片需要在module.json5配置文件中的extensionAbilities标签下&#xff0c;配置FormExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签&#xff0c;其中键名称…...

mac安装nacos,M1芯片

第一步&#xff0c;官网下载 》nacos官网 去github中下载对应的版本&#xff0c;本人下载的是1.4.1版本 在这儿选择其他的版本&#xff0c;下面这里选择 tar.gz 压缩包 解压后放到一个非中文的目录下&#xff0c;我选择在 user目录下面创建一个other目录&#xff0c;将使用的环…...

老板说把跳针改过去,什么是主板跳针

最近骑车拍了很多视频&#xff0c;把电脑磁盘堆满了&#xff0c;想着买一条固态SSD卡扩展一下。 一咬牙一跺脚&#xff0c;直接安排&#xff0c;毫不犹豫。顺带加装了无限网卡和蓝牙5.2。 收到后立马安装。安装完发现识别不到新磁盘 确认安装没问题。然后就去问固态硬盘的客服 …...

PyTorch代码实战入门

人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片&#xff0c;文件名就是数据的label 二、使用Dataset加载数据 打开pycharm&#xff0c;选择Anaconda创建的pytorch环…...

TSINGSEE青犀视频汇聚平台EasyCVR多种视频流播放协议介绍

众所周知&#xff0c;TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入&#xff0c;包括主流标准协议GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。今天我们来说一说&#xff0c;EasyCVR平台支持分…...

Vivado进行自定义IP封装

一. 简介 本篇文章将介绍如何使用Vivado来对上篇文章(FPGA驱动SPI屏幕)中的代码进行一个IP封装&#xff0c;Vivado自带的IP核应该都使用过&#xff0c;非常方便。 这里将其封装成IP核的目的主要是为了后续项目的调用&#xff0c;否则当我新建一个项目的时候&#xff0c;我需要将…...

开放自动化软件的硬件平台

自动化行业的产品主要以嵌入式系统为主&#xff0c;历来对产品硬件的可靠性和性能都提出很高的要求。最典型的产品要数PLC。PLC 要求满足体积小&#xff0c;实时性&#xff0c;可靠性&#xff0c;可扩展性强&#xff0c;环境要求高等特点。它们通常采用工业级高性能嵌入式SoC 实…...

AdvancedInstaller打包程序

文章目录 1. AdvancedInstaller 下载2. AdvancedInstaller 启动3. 新建工程4. 配置安装包详细信息5. 配置安装参数6. 添加要打包的文件7. 设置安装完成后启动程序8. 构建打包 1. AdvancedInstaller 下载 下载网址&#xff1a;https://www.advancedinstaller.com/ 2. AdvancedIn…...

无穷限积分习题

前置知识&#xff1a;无穷限积分 习题1 计算 ∫ 1 ∞ ln ⁡ x x 2 d x \int_1^{\infty}\dfrac{\ln x}{x^2}dx ∫1∞​x2lnx​dx 解&#xff1a; \qquad 原式 ( − ln ⁡ x x ) ∣ 1 ∞ ∫ 1 ∞ 1 x 2 d x ( − ln ⁡ x x ) ∣ 1 ∞ ( − 1 x ) ∣ 1 ∞ (-\dfrac{\…...

AI 3D结构光技术加持,小米引领智能门锁新标准

一直以来&#xff0c;小米智能门锁系列产品让更多家庭走进了安全便捷的智能生活&#xff0c;安全至上的设计让很多家庭都轻松告别了随身钥匙。 7月27日&#xff0c;小米正式推出小米智能门锁M20 Pro&#xff0c;再一次引领智能门锁产品的发展潮流。该款门锁采用AI 3D结构光技术…...

管理类联考——逻辑——形式逻辑——汇总篇

简述 形式逻辑&#xff1a; 识别题型&#xff1a;逻辑符号表达及标志词&#xff1a;联假言符号化特殊命题“除非否则”&#xff1b;五大关系&#xff1a;矛盾、等价、包含、至少有一真、至少有一假&#xff1b;【通过“关系”&#xff0c;串联起“假联选”言】 识别题型&…...

架构的分类

目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 ​二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...