結合して結果を集める
スレッドの終了を待ち、出力を集めます。
「結合して結果を集める」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。
スレッドを結合する理由
スレッドの起動によって処理は始まりますが、通常はその結果、または少なくとも処理が完了したことの確認が必要です。結合は、そのために待機する方法です。
このレッスンでは、ハンドルを集めて結合し、生成された値を最終的な答えにまとめます。
joinはResultを返す
joinはResultを返します。成功時にはクロージャの戻り値が入り、スレッドがパニックした場合はErrが入ります。
unwrapを呼び出すと値を直接取得できますが、スレッドが失敗するとパニックが伝播します。
use std::thread;
fn main() {
let h = thread::spawn(|| 7 * 6);
let value: i32 = h.join().unwrap();
println!("answer = {}", value);
}まずハンドルを集める
スレッドを並列に実行するには、どれかを結合する前にすべてを起動します。起動するたびに各ハンドルをベクターへ追加します。
起動ループ内で結合すると、次のスレッドが始まる前に各スレッドが終了するため、並行性が失われます。
use std::thread;
fn main() {
let handles: Vec<_> = (0..4)
.map(|i| thread::spawn(move || i * i))
.collect();
for h in handles {
println!("{}", h.join().unwrap());
}
}結果をVecに集める
すべてのハンドルを結合し、返された値を結果のベクターに追加できます。
ここでは起動順も結合順も0から3まで同じなので、結果のベクターは予測可能な順序になります。
use std::thread;
fn main() {
let handles: Vec<_> = (1..=4)
.map(|n| thread::spawn(move || n * 10))
.collect();
let results: Vec<i32> = handles
.into_iter()
.map(|h| h.join().unwrap())
.collect();
println!("{:?}", results);
}並列結果の合計
結果を集めたら、それらを縮約できます。ここでは各スレッドが部分的な値を計算し、mainがそれらを合計します。
このmapしてからreduceする形は、一般的な並列処理のパターンです。
use std::thread;
fn main() {
let handles: Vec<_> = (1..=5)
.map(|n| thread::spawn(move || n * n))
.collect();
let total: i32 = handles
.into_iter()
.map(|h| h.join().unwrap())
.sum();
println!("total = {}", total);
}処理をチャンクに分割する
大きなデータの場合はチャンクに分割し、各チャンクを専用のスレッドに渡します。各スレッドは部分合計を返します。
各チャンクを所有するVecにクローンすれば、スレッドがそのスライスを安全に所有できます。
use std::thread;
fn main() {
let data: Vec<i32> = (1..=10).collect();
let handles: Vec<_> = data
.chunks(5)
.map(|c| { let c = c.to_vec(); thread::spawn(move || c.iter().sum::<i32>()) })
.collect();
let total: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
println!("{}", total);
}スレッドのパニックに対処する
ワーカーがパニックする可能性がある場合は、無条件にunwrapしないでください。joinから返されたResultをmatchして、エラー経路を処理します。
これにより、1つのワーカーが失敗してもメインスレッドは動作を続けられます。
use std::thread;
fn main() {
let h = thread::spawn(|| {
if true { panic!("failed"); }
1
});
match h.join() {
Ok(v) => println!("got {}", v),
Err(_) => println!("thread panicked"),
}
}共有Vecに集める
値を返す代わりに、スレッドから共有Arc<Mutex<Vec>>へ追加することもできます。この場合、順序は非決定的になります。
すべてのスレッドを結合した後、ベクターを1回ロックして、集められたすべての結果を読み取ります。
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let out = Arc::new(Mutex::new(vec![]));
let mut handles = vec![];
for i in 0..3 {
let o = Arc::clone(&out);
handles.push(thread::spawn(move || o.lock().unwrap().push(i)));
}
for h in handles { h.join().unwrap(); }
println!("{:?}", out.lock().unwrap());
}スコープ付きスレッド
thread::scopeを使うと、moveやArcなしでローカルデータを借用するスレッドを作成できます。スコープは、戻る前にすべてのスレッドが終了することを保証します。
これにより、スタック上のデータを並列に読み取る処理を、簡潔かつ安全に記述できます。
use std::thread;
fn main() {
let data = vec![1, 2, 3];
thread::scope(|s| {
s.spawn(|| println!("sum {}", data.iter().sum::<i32>()));
s.spawn(|| println!("len {}", data.len()));
});
}スコープから結果を返す
スコープ付きスレッドも、ハンドルを通じて値を返せます。スコープ内でそれらを結合し、結果を集めます。
スコープは完了するまでブロックするため、借用したデータはその間ずっと有効です。
use std::thread;
fn main() {
let nums = vec![4, 5, 6];
let total = thread::scope(|s| {
let h = s.spawn(|| nums.iter().sum::<i32>());
h.join().unwrap()
});
println!("total = {}", total);
}結合を忘れない
JoinHandleを結合せずにドロップすると、スレッドはデタッチされます。実行は続きますが、結果を取得したり、完了を待機したりできなくなります。
デタッチされたスレッドが終了する前に、プロセス自体が終了することもあります。結果や完了の確認が必要な場合は結合してください。
クイックチェック
スレッドの結合と結果の収集についての理解度を確認しましょう。
まとめ
joinは、スレッドの値またはパニックを保持するResultを返すことを学びました。並行性を保つには、まずすべてのスレッドを起動してから結合します。
結果をVecに集めて縮約したり、共有Arc<Mutex<Vec>>へ追加したりできます。スコープ付きスレッドはローカルデータを安全に借用でき、ハンドルをドロップするとスレッドはデタッチされます。
これで、Rustにおけるスレッドと共有状態の基本的なツールが身につきました。
よくある質問
「結合して結果を集める」レッスンは無料ですか?
はい。「結合して結果を集める」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。
「結合して結果を集める」で何を学びますか?
スレッドの終了を待ち、出力を集めます。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Learn Rust Codingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「結合して結果を集める」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLearn Rust Codingレッスンでコードを書いて実行できますか?
はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- スレッドを生成する
- データをスレッドに移動する
- ArcとMutexで共有する
- 結合して結果を集める