やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのスレッド(Mutex、Arc)

 1度に1スレッドのみアクセス可にすることで整合性を保つ。

成果物

参考

Mutex

use std::sync::Mutex;
fn main() {
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("m = {:?}", m);
}
  • MutexMutexGuardというスマートポインタの一種を返す
  • MutexGuardDropでロック解除する

複数スレッド間

use std::sync::Mutex;
use std::thread;
fn main() {
    let counter = Mutex::new(0);
    let mut handles = vec![];
    for _ in 0..10 {
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}
$ rustc main.rs
error[E0382]: capture of moved value: `counter`
  --> main.rs:12:27
   |
11 |         let handle = thread::spawn(move || {
   |                                    ------- value moved (into closure) here
12 |             let mut num = counter.lock().unwrap();
   |                           ^^^^^^^ value captured here after move
   |
   = note: move occurs because `counter` has type `std::sync::Mutex<i32>`, which does not implement the `Copy` trait

error[E0382]: use of moved value: `counter`
  --> main.rs:20:29
   |
11 |         let handle = thread::spawn(move || {
   |                                    ------- value moved (into closure) here
...
20 |     println!("Result: {}", *counter.lock().unwrap());
   |                             ^^^^^^^ value used here after move
   |
   = note: move occurs because `counter` has type `std::sync::Mutex<i32>`, which does not implement the `Copy` trait

純化

use std::sync::Mutex;
use std::thread;
fn main() {
    let counter = Mutex::new(0);
    let mut handles = vec![];
    let handle = thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
    let handle2 = thread::spawn(move || {
        let mut num2 = counter.lock().unwrap();

        *num2 += 1;
    });
    handles.push(handle2);
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}
$ rustc main.rs
error[E0382]: capture of moved value: `counter`
  --> main.rs:17:24
   |
11 |     let handle = thread::spawn(move || {
   |                                ------- value moved (into closure) here
...
17 |         let mut num2 = counter.lock().unwrap();
   |                        ^^^^^^^ value captured here after move
   |
   = note: move occurs because `counter` has type `std::sync::Mutex<i32>`, which does not implement the `Copy` trait

error[E0382]: use of moved value: `counter`
  --> main.rs:25:29
   |
11 |     let handle = thread::spawn(move || {
   |                                ------- value moved (into closure) here
...
25 |     println!("Result: {}", *counter.lock().unwrap());
   |                             ^^^^^^^ value used here after move
   |
   = note: move occurs because `counter` has type `std::sync::Mutex<i32>`, which does not implement the `Copy` trait

 最初のスレッドでmoveしたためcounterの所有権がクロージャへムーブした。その後、2つ目スレッドのクロージャ内でcounterを使おうとしたため、「すでにムーブ後であり所有権がない」と怒られる。

Rc<T>でもダメ

use std::rc::Rc;
use std::sync::Mutex;
use std::thread;
fn main() {
    let counter = Rc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let counter = Rc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}
$ rustc main.rs
error[E0277]: `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
  --> main.rs:13:22
   |
13 |         let handle = thread::spawn(move || {
   |                      ^^^^^^^^^^^^^ `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
   |
   = help: within `[closure@main.rs:13:36: 16:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::sync::Mutex<i32>>`
   = note: required because it appears within the type `[closure@main.rs:13:36: 16:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
   = note: required by `std::thread::spawn`

 Rcで複数の所有を許してもエラー。Rc<T>はスレッド間セーフではない。

Arc<T>でスレッドセーフ

 Arc<T>Rc<T>のスレッドセーフ+内部可変性を加えたようなもの。Atomic Rc

use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("Result: {}", *counter.lock().unwrap());
}
$ rustc main.rs
$ ./main
Result: 10

注意

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで