自动解引用 Arc 的混淆

7
这是从Mutex文档中摘取的例子:

像这样,你可以使用互斥锁来确保在一个特定时间只有一个线程可以访问数据:

use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;

const N: usize = 10;
fn main() {
    let data = Arc::new(Mutex::new(0));
    let (tx,rx) = channel();
    for _ in 0..N{
        let (data, tx) = (data.clone(), tx.clone());
        thread::spawn(move || {
            // snippet
        });
    }
    rx.recv().unwrap();
}

我的问题是关于代码片段注释的位置。它被给定为:
let mut data = data.lock().unwrap();
*data += 1;
if *data == N {
    tx.send(()).unwrap();
}

数据类型为Arc<Mutex<usize>>,因此在调用data.lock()时,我假设Arc被自动解引用,并将usize分配给data。为什么我们需要再次在data前面加上*来解引用它?
下面的代码首先对Arc进行解引用,然后继续使用只有一个usize的片段,这也可以代替原始代码片段。
let mut data = *data.lock().unwrap();
data += 1;
if data == N {
    tx.send(()).unwrap();
}   
1个回答

16
跟随文档,从 Arc<T> 开始:
  • Arc::lock 存在吗?不存在。查看 Deref
  • Deref::TargetT。查看 Mutex<T>
  • Mutex::lock 存在吗?存在。它返回 LockResult<MutexGuard<T>>
  • unwrap 来自哪里?LockResult<T>Result<T, PoisonError<T>> 的同义词。因此,它是 Result::unwrap,它会得到一个 MutexGuard<T>
  • 因此,data 的类型是 MutexGuard<usize>

因此,这是错误的:

因此,在调用 data.lock() 时,我假设 Arc 自动解引用,并将一个 usize 赋给 data

因此,问题不在于为什么无法直接赋值,而在于你如何能够完全地分配一个 usize 值。再次跟随文档:

  • data 是一个 MutexGuard<usize>,因此查看 MutexGuard<T>
  • *data 是指针解引用,在需要修改的上下文中。查找 DerefMut 的实现。
  • 它说对于 MutexGuard<T>,它实现了 DerefMut::deref_mut(&mut self) -> &mut T
  • 因此,*data 的结果是 &mut usize
然后我们有您修改过的示例。此时,应该清楚地知道这完全没有做同样的事情:它正在改变一个本地变量,该变量恰好包含与互斥体相同的值。但是,因为它是一个本地变量,所以更改它对互斥体的内容没有任何影响。
因此,简短的版本是:锁定互斥体的结果是“智能指针”,它包装实际值,而不是值本身。因此,您必须解除引用才能访问该值。

1
我更喜欢你的答案。我猜你没有解决的唯一问题是在提供的代码片段中data是两种不同的类型。第一个是MutexGuard<T>,第二个只是一个整数变量。 - Simon Whitehead
@SimonWhitehead:啊,对了。我甚至没看到那个。 - DK.
@DK 谢谢您的解释。现在我明白了。 - Aravindh S

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