並行(並列、同時)に実行する。
成果物
スレッド
生成: 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
とあるのでそれを使うのか? ドキュメントのメニューを見ると別の項でやるっぽい。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
- rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ
- Rustのイテレータ
- Rustのイテレータ(Minigrep改善)
- Rustのイテレータ(パフォーマンス)
- Rustのイテレータ(Minigrep改善)
- Rustのcargo(ビルドのカスタマイズ)
- Rustのcargo(cargo docでドキュメント生成)
- Rustのエクスポート(pub use)
- Rustのクレートを公開する方法(crates.io)
- Rustのcargoでワークスペースをつくる
- Rustのcargo installでバイナリをインストールする
- Rustのcargoを拡張する方法
- Rustのスマートポインタ
- スマートポインタBox
- Rustのスマートポインタ(Derefトレイト)
- Rustのスマートポインタ(Dropトレイト)
- Rustのスマートポインタ(Rc
) - Rustのスマートポインタ(RefCell
) - Rustのスマートポインタ(Weak
)