使用Arc、线程与mpsc通道时出现‘Borrowed value does not live long enough’错误的技术咨询
我来帮你理清这个问题——你想用mpsc通道在线程间发送持有Manager引用的Data结构体,虽然用了Arc来共享Manager,但编译时碰到了生命周期错误,对吧?
先看你的代码和报错:
你的代码:
use std::sync::mpsc; use std::sync::Arc; use std::thread; struct Manager {} struct Data<'a> { manager: &'a Manager, } impl Manager { fn new() -> Self { Self {} } fn make(&self) -> Data { Data { manager: self } } } unsafe impl Send for Manager {} fn main() { let manager = Arc::new(Manager::new()); let (tx, _rx) = mpsc::channel(); let mut children = Vec::new(); for id in 0..2 { let thread_manager = Arc::clone(&manager); let thread_tx = tx.clone(); let child = thread::spawn(move || { let msg = thread_manager.make(); thread_tx.send(msg).unwrap(); println!("thread {} finished", id); }); children.push(child); } }
编译错误:
error[E0597]: `thread_manager` does not live long enough --> src/main.rs:36:23 | 33 | let thread_tx = tx.clone(); | --------- lifetime `'1` appears in the type of `thread_tx` ... 36 | let msg = thread_manager.make(); | ^^^^^^^^^^^^^^ borrowed value does not live long enough 37 | thread_tx.send(msg).unwrap(); | ------------------- argument requires that `thread_manager` is borrowed for `'1` 38 | println!("thread {} finished", id); 39 | }); | - `thread_manager` dropped here while still borrowed
你的困惑点我也get到了:你觉得用Arc共享了Manager,那Data里的引用应该和全局的manager同生命周期,但实际却报错了。
问题根源:引用绑定的是线程内的Arc实例,而非全局Manager
你这里混淆了Arc本身的生命周期和它内部指向的Manager的生命周期。
当你在线程里调用thread_manager.make()时,Data里的manager引用,其实是指向thread_manager这个Arc实例内部的Manager——而thread_manager是线程闭包里的局部变量,它的生命周期只限于这个线程函数执行期间。
mpsc通道的发送端要求,发送的消息必须能存活到发送端存在的整个周期(或者说,消息的生命周期要满足通道的泛型约束)。但你的Data里的引用绑定到了线程内的thread_manager,当线程函数执行完,thread_manager会被drop,这个引用就变成悬垂引用了——Rust的借用检查器绝对不允许这种情况,所以直接报错。
简单总结:Data持有的不是全局Manager的引用,而是线程内那个临时Arc实例所指向的Manager的引用,这个引用的生命周期和线程内的Arc绑定,根本没法脱离线程存活,自然没法通过通道发送出去。
解决办法:让Data持有Arc而非引用
既然要跨线程传递,与其让Data持有引用,不如让它持有Arc<Manager>的共享所有权。这样Data就不再依赖任何临时引用的生命周期,完全可以安全地在通道里传递。
修改后的代码如下:
use std::sync::mpsc; use std::sync::Arc; use std::thread; struct Manager {} // 去掉生命周期参数,换成Arc<Manager> struct Data { manager: Arc<Manager>, } impl Manager { fn new() -> Self { Self {} } // 修改方法接收者为&Arc<Self>,方便直接clone出Arc给Data fn make(self: &Arc<Self>) -> Data { Data { manager: Arc::clone(self) } } } // 这里不需要unsafe impl Send了——空结构体Manager自动实现Send/Sync,Arc本身也支持Send/Sync // unsafe impl Send for Manager {} fn main() { let manager = Arc::new(Manager::new()); let (tx, _rx) = mpsc::channel(); let mut children = Vec::new(); for id in 0..2 { let thread_manager = Arc::clone(&manager); let thread_tx = tx.clone(); let child = thread::spawn(move || { let msg = thread_manager.make(); thread_tx.send(msg).unwrap(); println!("thread {} finished", id); }); children.push(child); } }
这样修改后,Data自己持有Manager的共享所有权,它的生命周期不再依赖任何局部变量,完全符合mpsc通道的发送要求,编译就能顺利通过了。
内容的提问来源于stack exchange,提问作者Vaillant Etienne

