通常のスレッド

Rust のスレッドは、他の言語のスレッドと同様に動作します:

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        for i in 0..10 {
            println!("Count in thread: {i}!");
            thread::sleep(Duration::from_millis(5));
        }
    });

    for i in 0..5 {
        println!("Main thread: {i}");
        thread::sleep(Duration::from_millis(5));
    }
}
  • 新しいスレッドを生成しても、main の最後でプログラムの終了が自動的に 遅らされることはありません。
  • スレッドの panic は互いに独立しています。
    • panic はペイロードを持つことができ、その内容は Any::downcast_ref で取り出せます。
  • この例を実行します。

    • 5ms というタイミングには十分な余裕があるため、main スレッドと生成した スレッドはほぼ足並みをそろえて進みます。
    • 生成したスレッドが 10 に到達する前にプログラムが終了することに注目して ください。
    • これは main がプログラムを終了させ、生成したスレッドはプログラムを 存続させないためです。
      • 必要であれば、pthreads/C++ std::thread/boost::thread と比較して みてください。
  • 生成したスレッドが完了するまで待つには、どうすればよいでしょうか。

  • thread::spawnJoinHandle を返します。ドキュメントを見てください。

    • JoinHandle には、ブロックする .join() メソッドがあります。
  • let handle = thread::spawn(...) とし、後で handle.join() を使って スレッドの終了を待つと、プログラムは最後まで数えて 10 に到達します。

  • では、値を返したい場合はどうでしょうか。

  • もう一度ドキュメントを見てください:

    • thread::spawn のクロージャは T を返します
    • JoinHandle.join()thread::Result<T> を返します
  • handle.join()Result の戻り値を使って、返された値にアクセスします。

  • では、もう一方のケースはどうでしょうか。

    • スレッドで panic を発生させます。これによって main は panic しないことに 注目してください。
    • panic のペイロードにアクセスします。ここで Any について説明するのに ちょうどよいタイミングです。
  • これでスレッドから値を返せるようになりました。では、入力を受け取るには どうすればよいでしょうか。

    • スレッドのクロージャで何かを参照としてキャプチャします。
    • エラーメッセージは、それを move しなければならないことを示します。
    • それを move すると、計算してから派生した値を返せることがわかります。
  • 借用したい場合はどうでしょうか。

    • main は戻るときに子スレッドを終了させますが、別の関数であれば単に 戻って、それらを実行したままにしてしまいます。
    • それはスタック use-after-return であり、メモリ安全性に違反します!
    • これをどう避ければよいでしょうか。次のスライドを見てください。