无法将数据移出互斥锁

7
考虑以下代码示例,我有一个JoinHandlers的向量需要迭代并加入到主线程中,但是在这样做时,我会收到error: cannot move out of borrowed content错误。
let threads = Arc::new(Mutex::new(Vec::new()));

for _x in 0..100 {
     let handle = thread::spawn(move || {
          //do some work
     }

     threads.lock().unwrap().push((handle));
}

for t in threads.lock().unwrap().iter() {
     t.join();
}

4
你的代码中有未匹配的括号。 - oli_obk
在单线程环境中,为什么需要Arc Mutex呢? - some_engineer
3个回答

12
很遗憾,您无法直接完成此操作。当Mutex消耗您提供给它的数据结构时,您无法再次通过值获取它。您只能获取&mut引用,但这将不允许移出它。因此,即使使用into_iter()也行不通 - 它需要从MutexGuard获取的self参数。

然而,有一个解决方法。 您可以使用Arc<Mutex<Option<Vec<_>>>>代替Arc<Mutex<Vec<_>>>,然后只需从互斥锁中take()值即可:

for t in threads.lock().unwrap().take().unwrap().into_iter() {
}

如果值被移动到调用线程中,那么into_iter()将能够正常工作。

当然,您需要适当地构建向量并推入其中:

let threads = Arc::new(Mutex::new(Some(Vec::new())));
...
threads.lock().unwrap().as_mut().unwrap().push(handle);

然而,最好的方法是完全放弃Arc<Mutex<..>>层(当然,如果这个值不被其他线程使用)。

我已经尝试完全删除 Arc<Mutex<..>>,现在只剩下类似于 let mut threads = Vec::new(); threads.push(handle);for t in threads.iter() { 的内容,但是在 t.join() 上仍然出现了 error: cannot move out of borrowed content - Jacob Clark
1
你需要使用into_iter()而不是iter() - oli_obk
2
在不稳定的 Rust 上,除了使用Option技巧之外,你也可以使用split_off函数或者drain函数在Vec上进行操作。 - oli_obk
是的,如果 drain() 是稳定的话,那将是最好的解决方案(当然除了放弃 Arc<Mutex<..>>)。 - Vladimir Matveev
1
还可以简单地使用mem :: swap()mem :: replace(),并将其传递给Mutex内部对象的引用。 这应该可以在稳定的 Rust 上工作。 - Vaelden
显示剩余4条评论

6
在该问题的讨论中所提到的,现在在Rust中可以使用Arc::try_unwrap和Mutex.into_inner()方法来轻松地获取Arc>中的T,并不需要进行任何特殊处理。
    let threads = Arc::new(Mutex::new(Vec::new()));

    for _x in 0..100 {
         let handle = thread::spawn(move || {
              println!("{}", _x);
         });

         threads.lock().unwrap().push(handle);
    }

    let threads_unwrapped: Vec<JoinHandle<_>> = Arc::try_unwrap(threads).unwrap().into_inner().unwrap();
    for t in threads_unwrapped.into_iter() {
         t.join().unwrap();
    }

在这个沙盒环境中进行操作以进行验证。

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9d5635e7f778bc744d1fb855b92db178


-2
虽然排水是一个不错的解决方案,但你也可以做以下事情。
// with a copy 
let built_words: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let result: Vec<String> = built_words.lock().unwrap().clone();

// using drain 
let mut locked_result = built_words.lock().unwrap();
let mut result: Vec<String> = vec![];
result.extend(locked_result.drain(..));

我更喜欢克隆数据以获取原始值。不确定是否会有性能开销。


每个非原始类型的克隆(原始类型将被复制)都有一个开销。 - Alex Vergara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接