Rust中的并发:线程、协程、async/await — 完整解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Rust中的并发:从互斥锁到async/await



当谈到高性能系统时,Rust 立即成为焦点。但真正让它独一无二的是什么?这就是 Rust中的并发 — 一种让您获得C语言速度和内存安全,同时又不牺牲代码可读性的方法。在本文中,我们将解析三种主要工具:经典的线程、轻量级的协程(在Rust上下文中指异步任务)以及现代的async/await。开始吧!



为什么Rust不惧怕并发?

与许多语言不同,Rust 在编译器层面解决了数据竞争问题。所有权(ownership)和借用(borrowing)系统确保您不会意外地从两个线程同时修改数据。这不是魔法 — 这是严格的静态检查。正因如此,Rust中的多线程被认为是最安全的之一。



1. 线程 (std::thread)

经典的OS线程是基础。在Rust中,它们通过 std::thread 模块实现。每个线程都是一个独立的轻量级进程,可以在不同的CPU核心上并行执行。



示例:启动线程

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();}


这里 thread::spawn 创建一个新线程,而 join 等待其完成。很简单,但有一个问题:线程很“重”。每个线程至少消耗2-8 MB的栈内存。对于数千个并发任务来说,这并不合适。



何时使用线程?

  • 需要真正的并行(CPU密集型任务)。
  • 线程数量不超过核心数 * 2。
  • 处理阻塞操作(文件读取、同步调用)。


2. Rust中的协程:神话还是现实?

严格来说,Rust 没有像Go语言那样的协程。但有异步任务(tasks),它们的行为几乎相同:轻量级、协作式切换(无需OS参与),并且可以在一个线程上启动数千个。为此,需要使用运行时 Tokio 或 async-std。



示例:通过Tokio的异步任务(协程)

use tokio::task;

#[tokio::main]async fn main() { let handle = task::spawn(async { for i in 1..10 { println!("协程: {}", i); tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; } });

for i in 1..5 { println!("主任务: {}", i); tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; }

handle.await.unwrap();}


请注意:task::spawn 创建一个轻量级任务,它在Tokio的线程池中执行。每个任务的内存开销约为2-3 KB。这使得可以轻松启动100,000个“协程”。与Go不同,这里没有内置的调度器 — 您自己选择运行时。



Rust协程与Go协程有何不同?

  • 在Rust中 — 任务是协作式的:它们通过 .await 主动让出控制权。
  • 在Go中 — 协程是抢占式的:调度器在I/O点自动中断它们。
  • Rust 提供更多控制:您可以选择单线程或多线程运行时。


3. Async/await:异步Rust的核心

关键字 async 和 .await 将同步代码转换为异步代码,而无需

博客

书籍推荐