克隆Arc时会发生什么情况? [英] What happens when an Arc is cloned?

查看:34
本文介绍了克隆Arc时会发生什么情况?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习并发性,想澄清一下我对以下code example from the Rust book的理解。如果我说错了,请纠正我。

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));

    for i in 0..3 {
        let data = data.clone();
        thread::spawn(move || {
            let mut data = data.lock().unwrap();
            data[0] += i;
        });
    }

    thread::sleep(Duration::from_millis(50));
}

线路let data = data.clone()上发生了什么?

铁锈书上说

我们使用clone()创建一个新的自有句柄。然后将此句柄移动到新线程中。

新的"拥有的句柄"是什么?听起来像是对数据的引用?

由于clone获取&self并返回Self,每个线程是否修改原始数据而不是副本?我猜这就是代码没有使用data.copy()而是这里使用data.clone()的原因。

右侧的data为参照,左侧的data为自有值。这里有一个变量跟踪。

推荐答案

[...]let data = data.clone()上发生了什么?

Arc代表AR参考CArc管理一个对象(类型为T),并充当代理以允许共享所有权,这意味着:一个对象由多个名称拥有。哇,听起来很抽象,让我们把它分解一下!

共享所有权

假设您有一个Turtle🐢类型的对象,它是您为家人购买的。现在的问题是,你不能明确指定海龟的主人:每个家庭成员都有这种宠物!这意味着(抱歉,在这里是病态的),如果家庭中的一个成员死亡,海龟不会和那个家庭成员一起死。只有当所有的家庭成员都离开时,海龟才会死。每个人都拥有,最后一个清理干净

那么您将如何在Rust中表达这种共享所有权?您很快就会注意到,只使用标准方法是不可能的:您总是必须选择一个所有者,而其他所有人都只能引用这只乌龟。不太好!

因此出现了RcArc(就本文而言,它们的目的完全相同)。这些允许通过对不安全锈蚀进行一点修补来实现共享所有权。让我们看看执行以下代码后的内存(注意:内存布局是用于学习的,可能与现实世界中的内存布局不完全相同):

let annas = Rc::new(Turtle { legs: 4 });

内存:

  Stack                    Heap
  -----                    ----


  annas:
+--------+               +------------+
| ptr: o-|-------------->| count: 1   |
+--------+               | data: 🐢   |
                         +------------+

我们看到乌龟生活在堆上……在设置为1的计数器旁边。此计数器知道对象data当前有多少所有者。1是正确的:annas是目前唯一拥有这只乌龟的人。让我们clone()Rc获得更多所有者:

let peters = annas.clone();
let bobs = annas.clone();

现在内存如下所示:

  Stack                    Heap
  -----                    ----


  annas:
+--------+               +------------+
| ptr: o-|-------------->| count: 3   |
+--------+    ^          | data: 🐢   |
              |          +------------+
 peters:      |
+--------+    |
| ptr: o-|----+
+--------+    ^
              |
  bobs:       |
+--------+    |
| ptr: o-|----+
+--------+

如您所见,海龟仍然只存在一次。但引用计数增加了,现在是3,这是有道理的,因为这只海龟现在有三个主人。这三个所有者都引用堆上的这个内存块。这就是Rust书中所说的拥有句柄:这样的句柄的每个所有者也在某种程度上拥有底层对象。

(另见"Why is std::rc::Rc<> not Copy?")

原子性和突变性

您问Arc<T>Rc<T>有什么区别?Arc以原子方式递增和递减其计数器。这意味着多个线程可以同时递增和递减计数器,而不会出现问题。这就是为什么您可以跨线程边界发送Arc,但不能发送Rcs。

现在您注意到,您不能通过Arc<T>来更改数据!如果您的🐢失去了一条腿怎么办?Arc的设计不允许多个所有者(可能)同时进行可变访问。这就是为什么您经常看到Arc<Mutex<T>>这样的类型。Mutex<T>是一种提供内部可变性的类型,这意味着您可以从&Mutex<T>获得&mut T!这通常会与Rust核心原则冲突,但它是绝对安全的,因为互斥锁还管理访问:您必须请求访问对象。如果另一个线程/源当前可以访问该对象,则必须等待。因此,在某个给定时刻,只有一个线程能够访问T

结论

[...]是否每个线程都在修改原始数据而不是副本?

您可以从上面的解释中了解到:是的,每个线程都在修改原始数据。Arc<T>上的clone()不会克隆T,而只是创建另一个拥有的句柄;而该句柄又只是一个指针,其行为就像它拥有基础对象一样。

这篇关于克隆Arc时会发生什么情况?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆