一、异步并发控制:Semaphore、Mutex、RwLock的异步版本
1.1 为什么需要异步同步原语?
💡在同步编程中,我们使用std::sync::Mutex、std::sync::RwLock、std::sync::Semaphore等同步原语来控制并发访问。这些原语在多线程场景下非常有效,但在异步编程中,它们会导致任务阻塞,影响性能。
异步同步原语通过await关键字暂停任务,而不是阻塞线程,从而提高了CPU利用率。Tokio提供了一系列异步同步原语,如tokio::sync::Mutex、tokio::sync::RwLock、tokio::sync::Semaphore。
1.2 异步Mutex(互斥锁)
异步Mutex的使用方式与标准库的类似,但需要使用await来获取锁。
use tokio::sync::Mutex;
use std::sync::Arc;
#[tokio::main]
async fn main() {
// 创建异步Mutex,使用Arc实现共享所有权
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
// 创建10个任务,每个任务增加计数器的值
for i in 0..10 {
let counter_clone = counter.clone();
let handle = tokio::spawn(async move {
// 获取锁,使用await暂停任务直到锁可用
let mut guard = counter_clone.lock().await;
*guard += 1;
println!("Task {}: Counter = {}", i, *guard);
// 锁会在guard离开作用域时自动释放
});
handles.push(handle);
}
// 等待所有任务完成
for handle in handles {
handle.await.unwrap();
}
println!("Final counter: {}", *counter.lock().await);
}
AI写代码rust
运行
1234567891011121314151617181920212223242526272829
1.3 异步RwLock(读写锁)
异步RwLock允许多个读操作同时进行,但写操作需要独占访问。
use tokio::sync::RwLock;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
let mut handles = vec![];
// 创建5个读任务
for i in 0..5 {
let data_clone = data.clone();
let handle = tokio::spawn(async move {
let guard = data_clone.read().await;
println!("Read task {}: {:?}", i, *guard);
});
handles.push(handle);
}
// 创建1个写任务
let data_clone = data.clone();
let handle = tokio::spawn(async move {
let mut guard = data_clone.write().await;
guard.push(4);
println!("Write task: {:?}", *guard);
});
handles.push(handle);
// 等待所有任务完成
for handle in handles {
handle.await.unwrap();
}
println!("Final data: {:?}", *data.read().await);
}
AI写代码rust
运行
12345678910111213141516171819202122232425262728293031323334
1.4 异步Semaphore(信号量)
异步Semaphore用于控制同时访问某一资源的任务数量。
use tokio::sync::Semaphore;
use std::sync::Arc;
#[tokio::main]
async fn main() {
// 创建信号量,允许最多3个任务同时访问资源
let semaphore = Arc::new(Semaphore::new(3));
let mut handles = vec![];
// 创建10个任务
for i in 0..10 {
let semaphore_clone = semaphore.clone();
let handle = tokio::spawn(async move {
// 获取信号量许可
let permit = semaphore_clone.acquire().await.unwrap();
println!("Task {}: Accessing resource", i);
// 模拟访问资源的耗时
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
println!("Task {}: Done", i);
// 许可会在permit离开作用域时自动释放
});
handles.push(handle);
}
// 等待所有任务完成
for handle in handles {
handle.await.unwrap();
}
}
AI写代码rust
运行
1234567891011121314151617181920212223242526272829
1.5 同步原语的性能对比
| 同步原语 | 同步版本(std::sync) | 异步版本(tokio::sync) | 适用场景 |
|---|---|---|---|
| Mutex | 阻塞线程 | 暂停任务 | 保护共享数据的独占访问 |
| RwLock | 阻塞线程 | 暂停任务 | 保护共享数据的读写分离 |
| Semaphore | 阻塞线程 | 暂停任务 | 控制资源的并发访问数量 |
二、超时与取消的高级用法
2.1 多层超时
在复杂的异步操作中,我们可能需要设置多层超时。例如,整个操作设置一个超时,内部的子操作也设置一个更小的超时。
use tokio::time::{timeout, Duration};
async fn sub_operation() -> Result<String, String> {
tokio::time::sleep(Duration::from_secs(3)).await;
Ok("Sub operation completed".to_string())
}
async fn main_operation() -> Result<String, String> {
// 子操作设置2秒超时
let result = timeout(Duration::from_secs(2), sub_operation()).await;
match result {
Ok(Ok(msg)) => Ok(msg),
Ok(Err(e)) => Err(e),
Err(_) => Err("Sub operation timeout".to_string()),
}
}
#[tokio::main]
async fn main() {
// 整个操作设置4秒超时
let result = timeout(Duration::from_secs(4), main_operation()).await;
match result {
Ok(Ok(msg)) => println!("Success: {}", msg),
Ok(Err(e)) => println!("Error: {}", e),
Err(_) => println!("Main operation timeout"),
}
}
AI写代码rust
运行
123456789101112131415161718192021222324252627
2.2 取消信号传递
当一个任务被取消时,我们可能需要通知其内部的子任务也取消,以避免资源泄漏。
use tokio::sync::oneshot;
use tokio::time::sleep;
use std::time::Duration;
async fn sub_task(mut cancel_rx: oneshot::Receiver<()>) {
println!("Sub task started");
tokio::select! {
_ = sleep(Duration::from_secs(5)) => println!("Sub task completed"),
_ = &mut cancel_rx => println!("Sub task cancelled"),
}
}
async fn main_task() {
println!("Main task started");
let (cancel_tx, cancel_rx) = oneshot::channel();
let sub_handle = tokio::spawn(sub_task(cancel_rx));
// 模拟主任务运行3秒后取消
sleep(Duration::from_secs(3)).await;
println!("Main task cancelling sub task");
let _ = cancel_tx.send(()); // 发送取消信号
let _ = sub_handle.await; // 等待子任务完成
println!("Main task completed");
}
#[tokio::main]
async fn main() {
main_task().await;
}
AI写代码rust
运行
1234567891011121314151617181920212223242526272829
2.3 优雅取消与资源清理
在取消任务时,我们需要确保资源被正确清理。例如,关闭文件、释放连接等。
use tokio::sync::oneshot;
use tokio::time::sleep;
use std::time::Duration;
use std::fs::File;
use std::io::Write;
async fn file_operation(mut cancel_rx: oneshot::Receiver<()>) {
println!("Opening file...");
let mut file = File::create("test.txt").unwrap();
tokio::select! {
_ = sleep(Duration::from_secs(5)) => {
file.write_all(b"Data written successfully").unwrap();
println!("File operation completed");
},
_ = &mut cancel_rx => {
println!("File operation cancelled, cleaning up...");
// 这里可以添加资源清理代码
},
}
println!("File closed");
}
#[tokio::main]
async fn main() {
let (cancel_tx, cancel_rx) = oneshot::channel();
let handle = tokio::spawn(file_operation(cancel_rx));
// 模拟3秒后取消
sleep(Duration::from_secs(3)).await;
let _ = cancel_tx.send(());
let _ = handle.await;
}
AI写代码rust
运行
12345678910111213141516171819202122232425262728293031323334
三、异步编程设计模式
3.1 生产者-消费者模式
生产者-消费者模式是异步编程中最常用的模式之一。它通过一个共享的队列实现生产者和消费者之间的通信。
use tokio::sync::mpsc;
use tokio::time::sleep;
use std::time::Duration;
async fn producer(mut tx: mpsc::Sender<String>) {
for i in 0..5 {
let msg = format!("Message {}", i);
println!("Produced: {}", msg);
tx.send(msg).await.unwrap();
sleep(Duration::from_secs(1)).await;
}
// 关闭发送端
drop(tx);
}
async fn consumer(mut rx: mpsc::Receiver<String>) {
while let Some(msg) = rx.recv().await {
println!("Consumed: {}", msg);
sleep(Duration::from_secs(2)).await;
}
println!("Consumer finished");
}
#[tokio::main]
async fn main() {
// 创建通道,缓冲区大小为2
let (tx, rx) = mpsc::channel(2);
let producer_handle = tokio::spawn(producer(tx));
let consumer_handle = tokio::spawn(consumer(rx));
producer_handle.await.unwrap();
consumer_handle.await.unwrap();
println!("Main finished");
}
AI写代码rust
运行
1234567891011121314151617181920212223242526272829303132333435
3.2 事件驱动模式
事件驱动模式通过监听事件并在事件发生时触发相应的处理函数。在异步编程中,我们可以使用tokio::stream来实现事件驱动。
use tokio_stream::StreamExt;
use tokio::time::interval;
use std::time::Duration;
async fn event_handler(event: String) {
println!("Handling event: {}", event);
tokio::time::sleep(Duration::from_secs(1)).await;
println!("Event handled: {}", event);
}
#[tokio::main]
async fn main() {
// 创建事件流,每2秒产生一个事件
let event_stream = interval(Duration::from_secs(2))
.map(|instant| format!("Event at {:?}", instant));
// 处理事件
event_stream.for_each(|event| async move {
event_handler(event).await;
}).await;
}
AI写代码rust
运行
123456789101112131415161718192021
3.3 Actor模型 **
Actor模型是一种并发模型,每个Actor是一个独立的执行单元,通过消息传递进行通信。在Rust中,我们可以使用tokio::sync::oneshot或tokio::sync::mpsc实现简单的Actor。
use tokio::sync::{mpsc, oneshot};
use std::collections::HashMap;
// Actor的消息类型
enum ActorMessage {
Insert { key: String, value: String, reply: oneshot::Sender<()> },
Get { key: String, reply: oneshot::Sender<Option<String>> },
Remove { key: String, reply: oneshot::Sender<()> },
}
async fn actor(mut rx: mpsc::Receiver<ActorMessage>) {
let mut store = HashMap::new();
while let Some(msg) = rx.recv().await {
match msg {
ActorMessage::Insert { key, value, reply } => {
store.insert(key, value);
let _ = reply.send(());
},
ActorMessage::Get { key, reply } => {
let value = store.get(&key).cloned();
let _ = reply.send(value);
},
ActorMessage::Remove { key, reply } => {
store.remove(&key);
let _ = reply.send(());
},
}
}
}
struct ActorClient {
tx: mpsc::Sender<ActorMessage>,
}
impl ActorClient {
pub fn new(tx: mpsc::Sender<ActorMessage>) -> Self {
ActorClient { tx }
}
pub async fn insert(&self, key: String, value: String) {
let (reply_tx, reply_rx) = oneshot::channel();
self.tx.send(ActorMessage::Insert { key, value, reply: reply_tx }).await.unwrap();
reply_rx.await.unwrap();
}
pub async fn get(&self, key: String) -> Option<String> {
let (reply_tx, reply_rx) = oneshot::channel();
self.tx.send(ActorMessage::Get { key, reply: reply_tx }).await.unwrap();
reply_rx.await.unwrap()
}
pub async fn remove(&self, key: String) {
let (reply_tx, reply_rx) = oneshot::channel();
self.tx.send(ActorMessage::Remove { key, reply: reply_tx }).await.unwrap();
reply_rx.await.unwrap();
}
}
#[tokio::main]
async fn main() {
let (tx, rx) = mpsc::channel(32);
let _actor_handle = tokio::spawn(actor(rx));
let client = ActorClient::new(tx);
client.insert("key1".to_string(), "value1".to_string()).await;
println!("Get key1: {:?}", client.get("key1".to_string()).await);
client.insert("key2".to_string(), "value2".to_string()).await;
println!("Get key2: {:?}", client.get("key2".to_string()).await);
client.remove("key1".to_string()).await;
println!("Get key1 after remove: {:?}", client.get("key1".to_string()).await);
}
AI写代码rust
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
四、异步与同步的混合编程
4.1 在异步代码中调用同步代码
在异步代码中调用同步代码可能会导致任务阻塞,影响性能。Tokio提供了spawn_blocking函数,可以将同步代码放在单独的线程池中执行,避免阻塞异步任务。
use tokio::task::spawn_blocking;
fn sync_operation() -> String {
// 模拟耗时的同步操作
std::thread::sleep(std::time::Duration::from_secs(2));
"Sync operation completed".to_string()
}
async fn async_operation() -> String {
// 使用spawn_blocking执行同步代码
let result = spawn_blocking(sync_operation).await.unwrap();
println!("Sync operation result: {}", result);
"Async operation completed".to_string()
}
#[tokio::main]
async fn main() {
println!("Start");
let result = async_operation().await;
println!("Result: {}", result);
println!("End");
}
AI写代码rust
运行
12345678910111213141516171819202122
4.2 在同步代码中调用异步代码
在同步代码中调用异步代码需要使用block_on函数,它会阻塞当前线程直到异步操作完成。
use tokio::runtime::Runtime;
async fn async_operation() -> String {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
"Async operation completed".to_string()
}
fn sync_main() {
println!("Start");
// 创建一个Tokio运行时
let rt = Runtime::new().unwrap();
// 使用block_on阻塞当前线程直到异步操作完成
let result = rt.block_on(async_operation());
println!("Result: {}", result);
println!("End");
}
fn main() {
sync_main();
}
AI写代码rust
运行
1234567891011121314151617181920
4.3 混合编程的最佳实践
- 避免在异步任务中使用同步IO:同步IO会阻塞线程,影响性能。
- 使用spawn_blocking处理同步操作:将耗时的同步操作放在单独的线程池中执行。
- 限制block_on的使用:block_on会阻塞线程,应尽量避免在异步代码中使用。
- 合理设计架构:如果需要处理大量同步操作,考虑使用多线程架构而不是异步架构。
五、实战案例:构建异步消息队列 **系统
5.1 项目需求与架构设计
我们将构建一个简单的异步消息队列系统,支持以下功能:
- 生产者发送消息到队列
- 消费者从队列中消费消息
- 消息支持超时和重试机制
- 队列支持持久化存储(使用Redis)
- 支持多个生产者和消费者
项目架构设计:
- 使用Tokio作为异步运行时
- 使用Redis作为消息队列的存储介质
- 使用tokio::sync::mpsc实现生产者和消费者之间的通信
- 支持消息的超时和重试机制
5.2 依赖配置与项目初始化
创建项目:
cargo new rust-async-queue
cd rust-async-queue
AI写代码bash
12
在Cargo.toml中添加依赖:
[dependencies]
tokio = { version = "1.0", features = ["full"] }
redis = { version = "0.22", features = ["tokio-comp"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
AI写代码toml
12345
5.3 消息队列核心实现
消息结构
创建src/models.rs:
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Message {
pub id: String,
pub content: String,
pub retry_count: u32,
pub max_retries: u32,
pub created_at: chrono::DateTime<chrono::Utc>,
}
impl Message {
pub fn new(content: String, max_retries: u32) -> Self {
Message {
id: uuid::Uuid::new_v4().to_string(),
content,
retry_count: 0,
max_retries,
created_at: chrono::Utc::now(),
}
}
pub fn should_retry(&self) -> bool {
self.retry_count < self.max_retries
}
pub fn increment_retry_count(&mut self) {
self.retry_count += 1;
}
}
AI写代码rust
运行
123456789101112131415161718192021222324252627282930
队列核心功能
创建src/queue.rs:
use redis::AsyncCommands;
use serde_json;
use crate::models::Message;
pub struct AsyncQueue {
client: redis::Client,
queue_name: String,
dead_letter_queue: String,
}
impl AsyncQueue {
pub fn new(url: &str, queue_name: &str) -> Self {
AsyncQueue {
client: redis::Client::open(url).unwrap(),
queue_name: queue_name.to_string(),
dead_letter_queue: format!("{}:dead", queue_name),
}
}
pub async fn enqueue(&self, message: Message) -> Result<(), String> {
let mut conn = self.client.get_async_connection().await.map_err(|e| e.to_string())?;
let message_json = serde_json::to_string(&message).map_err(|e| e.to_string())?;
conn.rpush(&self.queue_name, message_json).await.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn dequeue(&self) -> Result<Option<Message>, String> {
let mut conn = self.client.get_async_connection().await.map_err(|e| e.to_string())?;
let result: Option<String> = conn.blpop(&self.queue_name, 5).await.map_err(|e| e.to_string())?.map(|( _, v)| v);
match result {
Some(message_json) => {
let message = serde_json::from_str(&message_json).map_err(|e| e.to_string())?;
Ok(Some(message))
},
None => Ok(None),
}
}
pub async fn enqueue_dead_letter(&self, message: Message) -> Result<(), String> {
let mut conn = self.client.get_async_connection().await.map_err(|e| e.to_string())?;
let message_json = serde_json::to_string(&message).map_err(|e| e.to_string())?;
conn.rpush(&self.dead_letter_queue, message_json).await.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn len(&self) -> Result<usize, String> {
let mut conn = self.client.get_async_connection().await.map_err(|e| e.to_string())?;
let len: usize = conn.llen(&self.queue_name).await.map_err(|e| e.to_string())?;
Ok(len)
}
}
AI写代码rust
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
生产者与消费者
创建src/producer.rs:
use crate::models::Message;
use crate::queue::AsyncQueue;
pub struct Producer {
queue: AsyncQueue,
}
impl Producer {
pub fn new(queue: AsyncQueue) -> Self {
Producer { queue }
}
pub async fn send(&self, content: String, max_retries: u32) -> Result<(), String> {
let message = Message::new(content, max_retries);
self.queue.enqueue(message).await
}
}
AI写代码rust
运行
1234567891011121314151617
创建src/consumer.rs:
use crate::models::Message;
use crate::queue::AsyncQueue;
pub struct Consumer {
queue: AsyncQueue,
handler: Box<dyn Fn(Message) -> Box<dyn std::future::Future<Output = Result<(), String>> + Send>>,
}
impl Consumer {
pub fn new(
queue: AsyncQueue,
handler: Box<dyn Fn(Message) -> Box<dyn std::future::Future<Output = Result<(), String>> + Send>>,
) -> Self {
Consumer { queue, handler }
}
pub async fn start(mut self) {
println!("Consumer started");
loop {
match self.queue.dequeue().await {
Ok(Some(mut message)) => {
println!("Consumed message: {}", message.content);
let result = (self.handler)(message.clone()).await;
match result {
Ok(()) => println!("Message processed successfully: {}", message.content),
Err(e) => {
println!("Error processing message: {} - {}", message.content, e);
if message.should_retry() {
message.increment_retry_count();
println!("Retrying message ({} of {}): {}", message.retry_count, message.max_retries, message.content);
self.queue.enqueue(message).await.unwrap();
} else {
println!("Message failed after {} retries: {}", message.max_retries, message.content);
self.queue.enqueue_dead_letter(message).await.unwrap();
}
},
}
},
Ok(None) => continue,
Err(e) => {
println!("Error dequeuing message: {}", e);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
},
}
}
}
}
AI写代码rust
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
5.4 应用程序入口
创建src/main.rs:
use tokio;
use crate::consumer::Consumer;
use crate::producer::Producer;
use crate::queue::AsyncQueue;
mod consumer;
mod models;
mod producer;
mod queue;
#[tokio::main]
async fn main() {
let queue = AsyncQueue::new("redis://127.0.0.1/", "test_queue");
// 创建生产者
let producer = Producer::new(queue.clone());
// 创建消费者
let consumer = Consumer::new(queue.clone(), Box::new(|message| {
Box::new(async move {
println!("Processing message: {}", message.content);
// 模拟处理消息的耗时
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// 随机返回成功或失败
if rand::random() {
Ok(())
} else {
Err("Processing failed".to_string())
}
})
}));
// 启动消费者
tokio::spawn(consumer.start());
// 生产者发送消息
for i in 0..10 {
let content = format!("Message {}", i);
producer.send(content, 3).await.unwrap();
println!("Sent message {}", i);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
// 等待程序结束
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
}
AI写代码rust
运行