第七篇: Rust 多线程与并发编程详解

Rust 多线程与并发编程详解

Rust 的并发模型基于"无畏并发"(Fearless Concurrency)理念,通过所有权系统在编译期防止数据竞争。

目录

  1. 线程基础
  2. 消息传递
  3. 共享状态并发
  4. 异步编程 (Async/Await)
  5. 并发模式与最佳实践
  6. 实战示例

线程基础

1. 创建线程

rust 复制代码
use std::thread;
use std::time::Duration;

fn main() {
    // 创建新线程
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("子线程: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    // 主线程
    for i in 1..5 {
        println!("主线程: {}", i);
        thread::sleep(Duration::from_millis(1));
    }

    // 等待子线程完成
    handle.join().unwrap();
}

2. 带返回值的线程

rust 复制代码
use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        // 线程执行计算
        let result = (1..=100).sum::<i32>();
        result  // 返回值
    });

    // 获取线程返回值
    let result = handle.join().unwrap();
    println!("计算结果: {}", result);
}

3. 捕获外部变量(move 闭包)

rust 复制代码
use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    // 使用 move 将所有权转移到线程
    let handle = thread::spawn(move || {
        println!("向量: {:?}", v);
    });

    handle.join().unwrap();
    // v 已被移动,不能再使用
}

4. 线程命名和构建器

css 复制代码
use std::thread;

fn main() {
    let builder = thread::Builder::new()
        .name("worker-thread".to_string())
        .stack_size(4 * 1024 * 1024);  // 4MB 栈

    let handle = builder.spawn(|| {
        println!("线程名: {:?}", thread::current().name());
        // 执行任务
    }).unwrap();

    handle.join().unwrap();
}

消息传递

Rust 使用 通道(Channel) 实现线程间通信,遵循"不要通过共享内存通信,而要通过通信来共享内存"。

1. 单生产者单消费者(mpsc)

rust 复制代码
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    // 创建通道
    let (tx, rx) = mpsc::channel();

    // 发送者线程
    thread::spawn(move || {
        let messages = vec![
            "消息1",
            "消息2",
            "消息3",
        ];

        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(100));
        }
    });

    // 接收者(主线程)
    for received in rx {
        println!("收到: {}", received);
    }
}

2. 多生产者单消费者

rust 复制代码
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    // 克隆发送端,创建多个生产者
    for i in 0..3 {
        let tx_clone = tx.clone();
        thread::spawn(move || {
            let msg = format!("线程 {} 的消息", i);
            tx_clone.send(msg).unwrap();
        });
    }

    // 显式丢弃原始发送端
    drop(tx);

    // 接收所有消息
    for received in rx {
        println!("收到: {}", received);
    }
}

3. 有界通道(同步通道)

rust 复制代码
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    // 创建容量为 2 的有界通道
    let (tx, rx) = mpsc::sync_channel(2);

    thread::spawn(move || {
        for i in 1..=5 {
            println!("发送: {}", i);
            tx.send(i).unwrap();  // 队列满时会阻塞
            println!("已发送: {}", i);
        }
    });

    thread::sleep(Duration::from_secs(2));

    for received in rx {
        println!("收到: {}", received);
        thread::sleep(Duration::from_millis(500));
    }
}

4. 通道选择(Crossbeam)

rust 复制代码
use crossbeam::channel;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx1, rx1) = channel::unbounded();
    let (tx2, rx2) = channel::unbounded();

    // 生产者1
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        tx1.send("来自通道1").unwrap();
    });

    // 生产者2
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(200));
        tx2.send("来自通道2").unwrap();
    });

    // 选择先到达的消息
    channel::select! {
        recv(rx1) -> msg => println!("收到: {:?}", msg),
        recv(rx2) -> msg => println!("收到: {:?}", msg),
    }
}

共享状态并发

1. 互斥锁(Mutex)

rust 复制代码
use std::sync::Mutex;
use std::thread;

fn main() {
    let counter = Mutex::new(0);
    let mut handles = vec![];

    for _ in 0..10 {
        let handle = thread::spawn(move || {
            // 这里有问题:counter 被移动了
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("结果: {}", *counter.lock().unwrap());
}

上面的代码会报错,需要使用 Arc

2. Arc + Mutex(正确的方式)

rust 复制代码
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // Arc: 原子引用计数,允许多个所有者
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("结果: {}", *counter.lock().unwrap());
}

3. RwLock(读写锁)

rust 复制代码
use std::sync::{Arc, RwLock};
use std::thread;

fn main() {
    let data = Arc::new(RwLock::new(vec![1, 2, 3]));
    let mut handles = vec![];

    // 多个读者
    for i in 0..5 {
        let data = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let r = data.read().unwrap();
            println!("读者 {}: {:?}", i, *r);
        });
        handles.push(handle);
    }

    // 一个写者
    let data_write = Arc::clone(&data);
    let writer = thread::spawn(move || {
        let mut w = data_write.write().unwrap();
        w.push(4);
        println!("写者添加了 4");
    });
    handles.push(writer);

    for handle in handles {
        handle.join().unwrap();
    }

    println!("最终数据: {:?}", *data.read().unwrap());
}

4. 原子类型(Atomic)

rust 复制代码
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let counter = Arc::new(AtomicUsize::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            for _ in 0..1000 {
                counter.fetch_add(1, Ordering::SeqCst);
            }
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("结果: {}", counter.load(Ordering::SeqCst));
}

5. 条件变量(Condvar)

rust 复制代码
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;

fn main() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair_clone = Arc::clone(&pair);

    // 等待线程
    thread::spawn(move || {
        let (lock, cvar) = &*pair_clone;
        let mut started = lock.lock().unwrap();
        
        while !*started {
            println!("等待信号...");
            started = cvar.wait(started).unwrap();
        }
        
        println!("收到信号,开始工作!");
    });

    thread::sleep(Duration::from_secs(1));

    // 通知线程
    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    *started = true;
    cvar.notify_one();
    println!("已发送信号");
}

异步编程

Rust 的异步编程基于 async/await 语法和 Future trait。

1. 基本 async/await

rust 复制代码
use tokio;

#[tokio::main]
async fn main() {
    let result = async_function().await;
    println!("结果: {}", result);
}

async fn async_function() -> i32 {
    println!("开始异步操作...");
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    println!("异步操作完成");
    42
}

2. 并发执行多个异步任务

rust 复制代码
use tokio;

#[tokio::main]
async fn main() {
    let task1 = tokio::spawn(async {
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        println!("任务1完成");
        1
    });

    let task2 = tokio::spawn(async {
        tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
        println!("任务2完成");
        2
    });

    // 等待所有任务完成
    let (result1, result2) = tokio::join!(task1, task2);
    println!("结果: {:?}, {:?}", result1, result2);
}

3. select! 宏(竞争执行)

rust 复制代码
use tokio;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    let task1 = async {
        sleep(Duration::from_secs(1)).await;
        "任务1"
    };

    let task2 = async {
        sleep(Duration::from_secs(2)).await;
        "任务2"
    };

    // 哪个先完成就返回哪个
    tokio::select! {
        result = task1 => println!("先完成: {}", result),
        result = task2 => println!("先完成: {}", result),
    }
}

4. 异步通道

rust 复制代码
use tokio::sync::mpsc;

#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel(100);

    // 发送者任务
    tokio::spawn(async move {
        for i in 1..=5 {
            tx.send(i).await.unwrap();
            println!("发送: {}", i);
        }
    });

    // 接收者
    while let Some(i) = rx.recv().await {
        println!("收到: {}", i);
    }
}

5. 异步互斥锁

rust 复制代码
use tokio::sync::Mutex;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    let data = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for i in 0..10 {
        let data = Arc::clone(&data);
        let handle = tokio::spawn(async move {
            let mut lock = data.lock().await;
            *lock += 1;
            println!("任务 {} 增加计数", i);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.await.unwrap();
    }

    println!("最终计数: {}", *data.lock().await);
}

并发模式与最佳实践

1. 线程池模式

rust 复制代码
use std::sync::{Arc, Mutex, mpsc};
use std::thread;

type Job = Box<dyn FnOnce() + Send + 'static>;

struct ThreadPool {
    workers: Vec<Worker>,
    sender: mpsc::Sender<Job>,
}

impl ThreadPool {
    fn new(size: usize) -> ThreadPool {
        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 }
    }

    fn execute<F>(&self, f: F)
    where
        F: 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, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
        let thread = thread::spawn(move || loop {
            let job = receiver.lock().unwrap().recv();

            match job {
                Ok(job) => {
                    println!("Worker {} 执行任务", id);
                    job();
                }
                Err(_) => {
                    println!("Worker {} 断开连接", id);
                    break;
                }
            }
        });

        Worker { id, thread }
    }
}

fn main() {
    let pool = ThreadPool::new(4);

    for i in 0..8 {
        pool.execute(move || {
            println!("任务 {} 执行", i);
            thread::sleep(std::time::Duration::from_secs(1));
        });
    }

    thread::sleep(std::time::Duration::from_secs(10));
}

2. Actor 模式

rust 复制代码
use tokio::sync::mpsc;

struct Actor {
    receiver: mpsc::Receiver<ActorMessage>,
    state: i32,
}

enum ActorMessage {
    Increment,
    Decrement,
    GetValue(tokio::sync::oneshot::Sender<i32>),
}

impl Actor {
    fn new(receiver: mpsc::Receiver<ActorMessage>) -> Self {
        Actor {
            receiver,
            state: 0,
        }
    }

    async fn run(mut self) {
        while let Some(msg) = self.receiver.recv().await {
            match msg {
                ActorMessage::Increment => {
                    self.state += 1;
                    println!("增加,当前值: {}", self.state);
                }
                ActorMessage::Decrement => {
                    self.state -= 1;
                    println!("减少,当前值: {}", self.state);
                }
                ActorMessage::GetValue(respond_to) => {
                    let _ = respond_to.send(self.state);
                }
            }
        }
    }
}

#[derive(Clone)]
struct ActorHandle {
    sender: mpsc::Sender<ActorMessage>,
}

impl ActorHandle {
    fn new() -> Self {
        let (sender, receiver) = mpsc::channel(8);
        let actor = Actor::new(receiver);
        tokio::spawn(actor.run());

        ActorHandle { sender }
    }

    async fn increment(&self) {
        let _ = self.sender.send(ActorMessage::Increment).await;
    }

    async fn decrement(&self) {
        let _ = self.sender.send(ActorMessage::Decrement).await;
    }

    async fn get_value(&self) -> i32 {
        let (send, recv) = tokio::sync::oneshot::channel();
        let _ = self.sender.send(ActorMessage::GetValue(send)).await;
        recv.await.unwrap()
    }
}

#[tokio::main]
async fn main() {
    let actor = ActorHandle::new();

    actor.increment().await;
    actor.increment().await;
    actor.decrement().await;

    let value = actor.get_value().await;
    println!("最终值: {}", value);
}

3. 生产者-消费者模式

rust 复制代码
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::sync_channel(5);  // 缓冲区大小为 5

    // 生产者
    let producer = thread::spawn(move || {
        for i in 1..=10 {
            println!("生产: {}", i);
            tx.send(i).unwrap();
            thread::sleep(Duration::from_millis(100));
        }
    });

    // 消费者
    let consumer = thread::spawn(move || {
        for item in rx {
            println!("消费: {}", item);
            thread::sleep(Duration::from_millis(300));
        }
    });

    producer.join().unwrap();
    consumer.join().unwrap();
}

4. 屏障同步(Barrier)

rust 复制代码
use std::sync::{Arc, Barrier};
use std::thread;

fn main() {
    let mut handles = Vec::with_capacity(5);
    let barrier = Arc::new(Barrier::new(5));

    for i in 0..5 {
        let c = Arc::clone(&barrier);
        handles.push(thread::spawn(move || {
            println!("线程 {} 准备中...", i);
            thread::sleep(std::time::Duration::from_millis(i * 100));
            
            // 等待所有线程到达屏障
            c.wait();
            
            println!("线程 {} 开始执行", i);
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

实战示例

示例 1:并发下载器

rust 复制代码
use std::sync::{Arc, Mutex};
use tokio;

struct DownloadManager {
    completed: Arc<Mutex<Vec<String>>>,
}

impl DownloadManager {
    fn new() -> Self {
        Self {
            completed: Arc::new(Mutex::new(Vec::new())),
        }
    }

    async fn download(&self, url: String) -> Result<(), Box<dyn std::error::Error>> {
        println!("开始下载: {}", url);
        
        // 模拟下载
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        
        // 记录完成
        let mut completed = self.completed.lock().unwrap();
        completed.push(url.clone());
        
        println!("下载完成: {}", url);
        Ok(())
    }

    async fn download_all(&self, urls: Vec<String>) {
        let mut tasks = vec![];

        for url in urls {
            let manager = self.clone();
            let task = tokio::spawn(async move {
                manager.download(url).await
            });
            tasks.push(task);
        }

        for task in tasks {
            let _ = task.await;
        }
    }

    fn get_completed(&self) -> Vec<String> {
        self.completed.lock().unwrap().clone()
    }
}

impl Clone for DownloadManager {
    fn clone(&self) -> Self {
        Self {
            completed: Arc::clone(&self.completed),
        }
    }
}

#[tokio::main]
async fn main() {
    let manager = DownloadManager::new();
    
    let urls = vec![
        "https://example.com/file1.zip".to_string(),
        "https://example.com/file2.zip".to_string(),
        "https://example.com/file3.zip".to_string(),
    ];

    manager.download_all(urls).await;

    println!("\n已完成下载:");
    for url in manager.get_completed() {
        println!("  - {}", url);
    }
}

示例 2:并发 Web 爬虫

rust 复制代码
use tokio::sync::{Semaphore, Mutex};
use std::sync::Arc;
use std::collections::HashSet;

struct WebCrawler {
    visited: Arc<Mutex<HashSet<String>>>,
    semaphore: Arc<Semaphore>,
}

impl WebCrawler {
    fn new(max_concurrent: usize) -> Self {
        Self {
            visited: Arc::new(Mutex::new(HashSet::new())),
            semaphore: Arc::new(Semaphore::new(max_concurrent)),
        }
    }

    async fn crawl(&self, url: String) {
        // 检查是否已访问
        {
            let mut visited = self.visited.lock().await;
            if visited.contains(&url) {
                return;
            }
            visited.insert(url.clone());
        }

        // 获取许可(限制并发数)
        let _permit = self.semaphore.acquire().await.unwrap();

        println!("爬取: {}", url);
        
        // 模拟网络请求
        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
        
        println!("完成: {}", url);
    }

    async fn crawl_all(&self, urls: Vec<String>) {
        let mut tasks = vec![];

        for url in urls {
            let crawler = self.clone();
            let task = tokio::spawn(async move {
                crawler.crawl(url).await;
            });
            tasks.push(task);
        }

        for task in tasks {
            let _ = task.await;
        }
    }

    async fn visited_count(&self) -> usize {
        self.visited.lock().await.len()
    }
}

impl Clone for WebCrawler {
    fn clone(&self) -> Self {
        Self {
            visited: Arc::clone(&self.visited),
            semaphore: Arc::clone(&self.semaphore),
        }
    }
}

#[tokio::main]
async fn main() {
    let crawler = WebCrawler::new(3);  // 最多3个并发

    let urls = (1..=10)
        .map(|i| format!("https://example.com/page{}", i))
        .collect();

    crawler.crawl_all(urls).await;

    println!("\n总共爬取了 {} 个页面", crawler.visited_count().await);
}

示例 3:本项目实际应用(数据库连接池)

rust 复制代码
// 这是本项目中 Tokio 异步编程的实际应用

use sqlx::MySqlPool;
use tokio;

// 数据库操作(异步)
async fn create_user(pool: &MySqlPool, username: &str, email: &str) -> Result<i64, sqlx::Error> {
    let result = sqlx::query(
        "INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)"
    )
    .bind(username)
    .bind(email)
    .bind("hashed_password")
    .execute(pool)
    .await?;

    Ok(result.0 as i64)
}

// 并发创建多个用户
async fn create_users_concurrently(pool: MySqlPool) {
    let mut tasks = vec![];

    for i in 1..=5 {
        let pool = pool.clone();  // 连接池可以克隆
        let task = tokio::spawn(async move {
            let username = format!("user{}", i);
            let email = format!("user{}@example.com", i);
            
            match create_user(&pool, &username, &email).await {
                Ok(id) => println!("创建用户 {} (ID: {})", username, id),
                Err(e) => eprintln!("错误: {}", e),
            }
        });
        tasks.push(task);
    }

    // 等待所有任务完成
    for task in tasks {
        task.await.unwrap();
    }
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPool::connect("mysql://root:password@localhost/test").await?;
    
    create_users_concurrently(pool).await;
    
    Ok(())
}

示例 4:缓存服务(本项目 Redis 应用)

rust 复制代码
use std::sync::Arc;
use tokio::sync::RwLock;
use std::collections::HashMap;

// 内存缓存(读多写少场景)
struct Cache {
    data: Arc<RwLock<HashMap<String, String>>>,
}

impl Cache {
    fn new() -> Self {
        Self {
            data: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    async fn get(&self, key: &str) -> Option<String> {
        let data = self.data.read().await;
        data.get(key).cloned()
    }

    async fn set(&self, key: String, value: String) {
        let mut data = self.data.write().await;
        data.insert(key, value);
    }

    async fn delete(&self, key: &str) {
        let mut data = self.data.write().await;
        data.remove(key);
    }
}

impl Clone for Cache {
    fn clone(&self) -> Self {
        Self {
            data: Arc::clone(&self.data),
        }
    }
}

#[tokio::main]
async fn main() {
    let cache = Cache::new();

    // 并发读写
    let mut tasks = vec![];

    // 写入任务
    for i in 0..5 {
        let cache = cache.clone();
        let task = tokio::spawn(async move {
            let key = format!("key{}", i);
            let value = format!("value{}", i);
            cache.set(key.clone(), value).await;
            println!("写入: {}", key);
        });
        tasks.push(task);
    }

    // 读取任务
    for i in 0..10 {
        let cache = cache.clone();
        let task = tokio::spawn(async move {
            let key = format!("key{}", i % 5);
            if let Some(value) = cache.get(&key).await {
                println!("读取: {} = {}", key, value);
            }
        });
        tasks.push(task);
    }

    for task in tasks {
        task.await.unwrap();
    }
}

同步原语对比

类型 用途 开销 使用场景
Mutex<T> 互斥访问 中等 共享可变状态
RwLock<T> 读写锁 较高 读多写少
Atomic* 原子操作 极低 简单计数器
Arc<T> 引用计数 共享只读数据
mpsc::channel 消息传递 中等 线程间通信
Barrier 屏障同步 等待所有线程
Condvar 条件变量 等待特定条件

异步 vs 线程

特性 线程 (Thread) 异步 (Async)
开销 较高(每个线程 ~2MB) 极低(每个任务 ~KB)
上下文切换 OS 调度 用户态调度
适用场景 CPU 密集型 I/O 密集型
阻塞操作 可以使用 不能阻塞
并发数量 有限(数百) 极大(数万)

最佳实践

1. 选择合适的并发模型

rust 复制代码
// CPU 密集型:使用线程
use std::thread;
let handle = thread::spawn(|| {
    // 计算密集型任务
    (1..1_000_000).sum::<i64>()
});

// I/O 密集型:使用异步
use tokio;
let result = tokio::spawn(async {
    // 网络请求、文件读写等
    fetch_data().await
});

2. 避免死锁

scss 复制代码
// ❌ 错误:可能死锁
let data1 = mutex1.lock().unwrap();
let data2 = mutex2.lock().unwrap();  // 如果其他线程先锁 mutex2

// ✅ 正确:统一锁顺序
fn safe_operation(mutex1: &Mutex<i32>, mutex2: &Mutex<i32>) {
    let data1 = mutex1.lock().unwrap();
    let data2 = mutex2.lock().unwrap();
    // 使用数据
}

3. 最小化锁持有时间

ini 复制代码
// ❌ 锁持有时间过长
{
    let mut data = mutex.lock().unwrap();
    expensive_computation(&mut data);
}

// ✅ 缩短锁持有时间
let result = expensive_computation_without_lock();
{
    let mut data = mutex.lock().unwrap();
    *data = result;
}

4. 使用 RAII 自动释放资源

csharp 复制代码
// 锁会在作用域结束时自动释放
{
    let data = mutex.lock().unwrap();
    // 使用 data
}  // 锁自动释放

总结

核心概念

  1. 所有权系统防止数据竞争 - 编译期保证
  2. Send 和 Sync trait - 控制跨线程传递
  3. 消息传递优于共享内存 - Channel 优先
  4. 零成本抽象 - 性能无损失

常用模式

  • Arc + Mutex - 共享可变状态
  • mpsc::channel - 线程间通信
  • tokio::spawn - 异步并发
  • Semaphore - 限制并发数

本项目应用

  • Tokio 异步运行时 - #[tokio::main]
  • 数据库连接池 - 多线程安全的 MySqlPool
  • Redis 连接管理 - Arc<RedisCache>
  • 并发请求处理 - Axum 异步 handlers
  • gRPC 流式通信 - tokio_stream

延伸阅读

相关推荐
猛喝威士忌1 小时前
Tauri 和 enigo 你们不许再崩溃啦!
rust·客户端
q***31893 小时前
数据库操作与数据管理——Rust 与 SQLite 的集成
数据库·rust·sqlite
百锦再14 小时前
第11章 泛型、trait与生命周期
android·网络·人工智能·python·golang·rust·go
芳草萋萋鹦鹉洲哦14 小时前
【Windows】tauri+rust运行打包工具链安装
开发语言·windows·rust
s91236010117 小时前
【Rust】m2 mac 编译linux 、aarch、win 程序
rust
Source.Liu17 小时前
【ISO8601库】日期时间解析器测试套件详解(tests.rs)
rust·time·iso8601
alwaysrun18 小时前
Rust中数组简介
rust·数组·array·切片
百锦再18 小时前
第12章 测试编写
android·java·开发语言·python·rust·go·erlang
s91236010121 小时前
【Rust】时间轮的数据结构于设计模式
rust