Rust线程任务分配不均问题的原因排查与解决
Rust线程任务分配不均问题排查
我正在学习Rust,写了一段多线程代码,发现其中一个线程执行次数远多于另一个,任务分配不均。相同逻辑的C++代码运行正常,求找出Rust代码中的问题。
原代码
use std::fmt; use std::sync::{Arc, Mutex}; use std::thread; use tokio::time::Duration; struct Client { id: i32, } impl fmt::Display for Client { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Client id: {:?}", self.id) } } fn main() { let client: Client = Client { id: 0 }; let counter = Arc::new(Mutex::new(client)); let mut handles = vec![]; // 线程1 let safe_client_1 = Arc::clone(&counter); let thread_1 = thread::spawn(move || loop { let mut num = safe_client_1.lock().unwrap(); num.id += 1; print!("thread 1: {}", num); thread::sleep(Duration::from_secs(1)); }); handles.push(thread_1); // 线程2 let safe_client_2 = Arc::clone(&counter); let thread_2 = thread::spawn(move || loop { let mut num = safe_client_2.lock().unwrap(); num.id -= 1; print!("thread 2: {}", num); thread::sleep(Duration::from_secs(1)); }); loop {} }
原输出
thread 1: Client id: 1 thread 1: Client id: 2 thread 2: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 2: Client id: -2 thread 2: Client id: -3 thread 2: Client id: -4 thread 2: Client id: -5 thread 2: Client id: -6 thread 2: Client id: -7 thread 1: Client id: -6 thread 2: Client id: -7 thread 2: Client id: -8 thread 2: Client id: -9 thread 2: Client id: -10 thread 2: Client id: -11 thread 2: Client id: -12 thread 2: Client id: -13 thread 2: Client id: -14 thread 2: Client id: -15 thread 2: Client id: -16 thread 2: Client id: -17 thread 2: Client id: -18 thread 2: Client id: -19 thread 2: Client id: -20 thread 2: Client id: -21 thread 2: Client id: -22 thread 2: Client id: -23 thread 2: Client id: -24 thread 2: Client id: -25 thread 2: Client id: -26 thread 2: Client id: -27
问题原因
核心问题在于MutexGuard的生命周期管理。原代码中,num作为MutexGuard变量,其生命周期覆盖了整个loop迭代,包括thread::sleep的1秒时间。这意味着线程在休眠时仍然持有互斥锁,另一个线程完全无法获取锁,只能等待当前线程的loop迭代结束、num被销毁后才能抢到锁。这种设计导致了一个线程长时间占据锁,另一个线程只有极小的概率在锁释放瞬间抢到执行权,最终出现任务分配极度不均的现象。
解决代码
通过手动调用drop(num)提前释放MutexGuard,确保在休眠前就释放锁,让另一个线程有机会获取锁并执行任务:
use std::fmt; use std::sync::{Arc, Mutex}; use std::thread; use tokio::time::Duration; struct Client { id: i32, } impl fmt::Display for Client { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "Client id: {:?}", self.id) } } fn main() { let client: Client = Client { id: 0 }; let counter = Arc::new(Mutex::new(client)); // 线程1 let safe_client_1 = Arc::clone(&counter); let thread_1 = thread::spawn(move || loop { let mut num = safe_client_1.lock().unwrap(); num.id += 1; print!("thread 1: {}", num); drop(num); // 提前释放锁 thread::sleep(Duration::from_secs(1)); }); // 线程2 let safe_client_2 = Arc::clone(&counter); let thread_2 = thread::spawn(move || loop { let mut num = safe_client_2.lock().unwrap(); num.id -= 1; print!("thread 2: {}", num); drop(num); // 提前释放锁 thread::sleep(Duration::from_secs(1)); }); loop {} }
解决后输出
thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0 thread 1: Client id: 1 thread 2: Client id: 0 thread 2: Client id: -1 thread 1: Client id: 0
内容的提问来源于stack exchange,提问作者Claudio Silva
相关产品推荐
相关产品推荐

