やってみる

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

Rustのスレッド

 並行(並列、同時)に実行する。

成果物

スレッド

生成: spawn()

main.rs

use std::thread;
use std::time::Duration;
fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("Thread: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 1..5 {
        println!("Main: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}
$ rustc main.rs
$ ./main
Main: 1
Thread: 1
Main: 2
Thread: 2
Main: 3
Thread: 3
Main: 4
Thread: 4
Thread: 5
$ ./main
Main: 1
Thread: 1
Main: 2
Thread: 2
Main: 3
Thread: 3
Main: 4
Thread: 4
thread '<unnamed>' panicked at 'cannot access stdout during shutdown
  • 並行に実行している
  • Mainが終了するとThreadも終了している

待機: join()

use std::thread;
use std::time::Duration;
fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("Thread: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    for i in 1..5 {
        println!("Main: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
    handle.join().unwrap();
}
$ ./main
Main: 1
Thread: 1
Main: 2
Thread: 2
Main: 3
Thread: 3
Main: 4
Thread: 4
Thread: 5
Thread: 6
Thread: 7
Thread: 8
Thread: 9
  • 並行に実行している
  • Mainが終了してもThreadが完了するまで終了しない(待機)

 join()をMainのforより前にすると、Threadが完了するまで待機する。

fn main() {
    let handle = thread::spawn(|| {
        // 略
    });
    handle.join().unwrap();
    for i in 1..5 {
        // 略
    }
}
$ ./main
Thread: 1
Thread: 2
Thread: 3
Thread: 4
Thread: 5
Thread: 6
Thread: 7
Thread: 8
Thread: 9
Main: 1
Main: 2
Main: 3
Main: 4
  • 順番に実行している(並列にならない)

 join()の位置に注意せねば並列にならない罠。

moveクロージャ

 所有権をムーブすることで、クロージャの親スコープにある変数を使える。

use std::thread;
fn main() {
    let v = vec![1, 2, 3];
    let handle = thread::spawn(|| {
        println!("vec: {:?}", v);
    });
    handle.join().unwrap();
}
$ rustc main.rs
error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
  --> main.rs:9:32
   |
9  |     let handle = thread::spawn(|| {
   |                                ^^ may outlive borrowed value `v`
10 |         println!("vec: {:?}", v);
   |                               - `v` is borrowed here
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
   |
9  |     let handle = thread::spawn(move || {
   |                                ^^^^^^^

 上記エラーのヘルプにmoveの記法まで書いてある。この通りに修正すると以下。

use std::thread;
fn main() {
    let v = vec![1, 2, 3];
    let handle = thread::spawn(move || {
        println!("vec: {:?}", v);
    });
    handle.join().unwrap();
}
$ rustc main.rs
$ ./main
vec: [1, 2, 3]

 moveして所有権を渡してしまったら、もうmainスコープでvを変更できない。

use std::thread;
fn main() {
    let v = vec![1, 2, 3];
    let handle = thread::spawn(move || {
        println!("vec: {:?}", v);
    });
    drop(v); // 所有権がないため不可。error[E0382]: use of moved value: `v`
    handle.join().unwrap();
}

疑問

 ドキュメントは上記で説明を終えている。でも、もしスレッドでpushして、全スレッド完了後にdropしたくなったらどうするの? 複数に所有権をもたせるRc<T>を使うの?

 試しにやってみたらエラーになった。

use std::thread;
use std::rc::Rc;
fn main() {
    let v = Rc::new(vec![1, 2, 3]);
    let handle = thread::spawn(|| {
        let vv = Rc::clone(&v);
        vv.push(0);
        println!("vec: {:?}", v);
    });
    handle.join().unwrap();
    drop(v);
}
$ rustc main.rs
error[E0277]: `std::rc::Rc<std::vec::Vec<i32>>` cannot be shared between threads safely
 --> main.rs:9:18
  |
9 |     let handle = thread::spawn(|| {
  |                  ^^^^^^^^^^^^^ `std::rc::Rc<std::vec::Vec<i32>>` cannot be shared between threads safely
  |
  = help: the trait `std::marker::Sync` is not implemented for `std::rc::Rc<std::vec::Vec<i32>>`
  = note: required because of the requirements on the impl of `std::marker::Send` for `&std::rc::Rc<std::vec::Vec<i32>>`
  = note: required because it appears within the type `[closure@main.rs:9:32: 14:6 v:&std::rc::Rc<std::vec::Vec<i32>>]`
  = note: required by `std::thread::spawn`

 「スレッド間で安全に共有できません」と怒られる。エラーにあるヘルプにSyncとあるのでそれを使うのか? ドキュメントのメニューを見ると別の項でやるっぽい。

対象環境

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

前回まで