9.1 多线程共享计数器/状态
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let mut handles = Vec::new(); // 存储线程句柄的向量
let counter = Arc::new(Mutex::new(0)); // 计数器,初始值为0
for _ in 0..100 {
let count = Arc::clone(&counter); // 克隆计数器,创建一个新的引用计数器
handles.push(thread::spawn(move || {
let mut c = count.lock().unwrap(); // 获取计数器的互斥锁,确保线程安全
*c += 1; // 增加计数器的值
println!("目前计数为:{}", *c);
}));
}
for handle in handles {
handle.join().unwrap(); // 等待线程完成执行
}
}
9.2 异步任务间共享状态 (Tokio/Async)
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let mut handles = Vec::new(); // 存储异步任务的句柄
let counter = Arc::new(Mutex::new(0)); // 计数器,初始值为0
for _ in 0..100 {
let count = Arc::clone(&counter); // 克隆计数器,创建一个新的引用计数器
handles.push(tokio::spawn(async move {
// 启动异步任务
let mut c = count.lock().await; // 获取计数器的互斥锁,确保线程安全
*c += 1; // 增加计数器的值
println!("目前计数为:{}", *c);
// 模拟耗时操作(持有锁期间尽量避免,此处仅演示)
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}));
}
for handle in handles {
handle.await.unwrap(); // 等待异步任务完成执行
}
}
9.3 后台线程与主线程通信
use std::sync::{Arc, Mutex};
use std::thread::{self, spawn};
use std::time;
fn main() {
let mut handles = Vec::new(); // 存储线程任务的句柄
let datalist = Arc::new(Mutex::new(Vec::new())); // 数据列表,初始值为空
for i in 0..10 { // 启动10个线程
let data = Arc::clone(&datalist); // 克隆数据列表,每个线程都有自己的数据副本
handles.push(spawn(move || {
let mut d = data.lock().unwrap(); // 获取数据列表的可变锁,用于修改数据
d.push(format!("{}线程的消息!", i)); // 向数据列表中添加线程的消息
thread::sleep(time::Duration::from_millis(100)); // 模拟线程执行时间
}));
}
for handle in handles {
handle.join().unwrap(); // 等待所有线程完成执行
}
println!("线程传来的数据:{:?}", datalist.lock().unwrap()); // 打印所有线程的消息
}
9.4 单例模式或全局配置
use std::sync::{Arc, Mutex, OnceLock};
use std::thread;
use std::time;
#[derive(Debug)]
struct Config { // 应用配置结构体
app_name: String,
app_version: u16,
}
impl Config {
fn load() -> Self { // 加载应用配置
println!("正在从文件或环境变量中,加载配置...");
Config {
app_name: "MyApp".to_string(),
app_version: 1,
}
}
}
static APP_CONFIG: OnceLock<Arc<Mutex<Config>>> = OnceLock::new(); // 应用配置单例
fn get_config() -> Arc<Mutex<Config>> { // 获取应用配置
APP_CONFIG
.get_or_init(|| { // 初始化应用配置
let config = Config::load(); // 加载应用配置
Arc::new(Mutex::new(config)) // 返回应用配置的Arc<Mutex<Config>>
})
.clone() // 返回应用配置的Arc<Mutex<Config>>
}
fn main() {
let mut handles = Vec::new(); // 存储线程任务的句柄
for i in 0..10 { // 启动10个线程
let config = get_config(); // 克隆应用配置的Arc<Mutex<Config>>
handles.push(thread::spawn(move || {
{
let mut cfg = config.lock().unwrap(); // 获取应用配置的可变锁,用于修改应用配置
cfg.app_version += 1; // 应用配置版本号增加
}
thread::sleep(time::Duration::from_millis(100)); // 模拟线程执行时间
}));
}
for handle in handles {
handle.join().unwrap(); // 等待所有线程完成执行
}
println!("当前配置信息:{:?}", *get_config().lock().unwrap());
}
9.5 关键注意事项
- 死锁风险 :
- 如果在持有锁期间调用其他可能再次获取同一锁的代码,会导致死锁。
- 避免:尽量保持锁的粒度小,不要在持锁时执行复杂逻辑或 I/O。
- 性能瓶颈 :
Mutex 是串行化的瓶颈。如果写操作非常频繁,线程会大量时间花在等待锁上。
- 优化 :
- 如果读多写少 ,考虑使用
Arc<RwLock<T>>。RwLock 允许并发读取,仅在写入时独占。
- 如果数据是简单的原子类型(如计数器),考虑使用
Arc<AtomicUsize>,无锁且更快。
- 异步环境陷阱 :
- 在
async 函数中,严禁 使用 std::sync::Mutex::lock(),因为它会阻塞整个异步运行时的工作线程。
- 正确做法 :使用
tokio::sync::Mutex 或 std::sync::Mutex 配合 tokio::task::spawn_blocking。
- 克隆开销 :
Arc::clone() 只是增加引用计数,开销极小(原子操作),可以放心在循环中克隆。