1. ホーム

[解決済み】アークがクローン化されるとどうなるの?

2022-04-16 15:19:23

質問

私は並行処理を学んでいますが、以下の点について理解を深めたいと思います。 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() ?

Rustの本には、次のように書かれています。

を使用します。 clone() を使用して、新しい所有ハンドルを作成します。そして、このハンドルは新しいスレッドに移動されます。

新しい "所有ハンドル" とは何ですか?データへの参照のようですが?

から clone&self を返し Self は、各スレッドがコピーではなく、元のデータを変更しているのでしょうか?そのためか、このコードでは data.copy() しかし data.clone() をここで紹介します。

は、その data は参照、右側の data は所有する値です。ここには変数のシャドウイングがあります。

どのように解決するのですか?

<ブロッククオート

[で何が起こっているのか? let data = data.clone() ?

Arc A トミー的に R 参照 C をオミットした。An Arc 管理する 1 オブジェクト(型は T ) を可能にするプロキシとして機能します。 共有オーナーシップ つまり、1つのオブジェクトを複数の名前で所有することができます。抽象的な話になってしまいましたが、分解してみましょう。

共有オーナーシップ

というタイプのオブジェクトがあるとします。 Turtle 家族のために買ったものです。ここで、このカメの明確な所有者を決められないという問題が発生します。つまり、(病的な話で恐縮ですが)家族の一人が死んでも、その亀はその家族と一緒に死ねないのです。カメが死ぬのは、家族全員が死んでしまった場合だけなのです。 みんなで持ち寄り、最後の一人が片付ける .

では、そのような共有のオーナーシップをRustで表現するにはどうしたらいいのでしょうか?標準的なメソッドだけでは無理なことにすぐに気がつくでしょう。常に所有者を一人選ばなければならず、他の人はそのカメへの参照しか持てないからです。良くないことです。

そこで RcArc (この話の中では、全く同じ目的です)。これらは、unsafe-Rustを少しいじることで、共有オーナーシップを可能にします。次のコードを実行した後のメモリを見てみましょう ( ノート : メモリレイアウトは学習用であり、実世界と全く同じメモリレイアウトを表すとは限りません)。

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 になっています。これは理にかなっていて、turtle には現在 3 人の所有者がいます。なぜなら、turtle には現在 3 人のオーナーがいるからです。この 3 人のオーナーはすべて、ヒープ上のこのメモリーブロックを参照しています。これをRustの本では 所有ハンドル このようなハンドルの各所有者は、その下にあるオブジェクトも所有しているようなものです。

( を参照してください。 なぜ std::rc::Rc<> not Copy?" )

アトミック性とミュータビリティ

とは何が違うのでしょうか? Arc<T>Rc<T> ということですか?その Arc はアトミックな方法でカウンターをインクリメントおよびデクリメントします。つまり、複数のスレッドが同時にカウンターをインクリメント、デクリメントしても問題ないのです。そのため Arc はスレッド境界を越えることができますが Rc s.

今、あなたが気づいたのは、データを変異させるには Arc<T> ! もし、あなたの足がなくなったら? Arc は、複数の所有者から同時に(おそらく)ミュータブルなアクセスができるように設計されていません。そのため、しばしば Arc<Mutex<T>> . その Mutex<T> を提供するタイプです。 内部可変型性 を取得することができる、ということです。 &mut T から &Mutex<T> ! これは通常、Rust のコアとなる原則と矛盾しますが、ミューテックスがアクセスも管理するため、完全に安全です。もし、他のスレッドやソースがそのオブジェクトにアクセスしている場合は、待つ必要があります。したがって、ある時点では、このオブジェクトにアクセスできるスレッドは 1 つだけです。 T .

まとめ

[各スレッドがコピーではなく、元のデータを修正しているのか?

上記の説明でご理解いただけると思いますが、そうです、各スレッドは元のデータを修正しているのです。A clone() オンザ Arc<T> をクローンすることはありません。 T が、単に別の オーナーズハンドル これは、あたかもそのオブジェクトを所有しているかのように振る舞う単なるポインタです。