グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



安全性、速度、並行性を兼ね備えた言語と、巷でうわさのRustを覗いてみる(スレッド編 その1)

みなさまお久しぶりです。

今回も「The Rust Programming Language」を読みながら、 スレッドについて見ていきたいと思います。

最初のサンプルを動かす

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

fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

結果はこのようになりました。

hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the spawned thread!
hi number 3 from the main thread!
hi number 4 from the spawned thread!
hi number 4 from the main thread!
hi number 5 from the spawned thread!

Process finished with exit code 0

もう一度実行すると、また別の出力になると思われます。

thread::sleep(Duration::from_millis(1))にてスレッドを停止させるので、 OSによって実行の順番がスケジュールされているのがわかります。

何度か実行すると、私の環境ではspawned thredのnumber 5が表示されたりされなかったりします。
これは、作成したスレッドの処理が終わる前に、メインスレッドが終了してしまうからだと思われます。

スレッドの終了を待つ

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

スレッドを作成したときにJoinHandle<T>を変数にいれておき、joinを呼び出すことでスレッドの終了を待ちます。

hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!

作成したスレッドが9までカウントされるので、メインスレッドが作成したスレッドの終了を待っていることが確認できます。

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap();

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

joinの位置を変えてみます。

hi number 1 from the spawned thread!
hi number 2 from the spawned thread!
hi number 3 from the spawned thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
hi number 6 from the spawned thread!
hi number 7 from the spawned thread!
hi number 8 from the spawned thread!
hi number 9 from the spawned thread!
hi number 1 from the main thread!
hi number 2 from the main thread!
hi number 3 from the main thread!
hi number 4 from the main thread!

スレッドを生成した直後に処理を待ってから、メインスレッドの残りの処理を実行しているのが確認できます。

複数のスレッドを作ってみる

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

fn main() {
    let handle1 = thread::spawn(|| {
        for _ in 0..10 {
            println!("スレッド1");
            thread::sleep(Duration::from_millis(1));
        }
    });

    let handle2 = thread::spawn(|| {
        for _ in 0..10 {
            println!("スレッド2");
            thread::sleep(Duration::from_millis(1));
        }
    });

    // スレッド1の終了を待つ
    handle1.join().unwrap();
    // スレッド2の終了を待つ
    handle2.join().unwrap();
}

複数のスレッドも生成してみます。

スレッド1
スレッド2
スレッド1
スレッド2
スレッド2
スレッド1
~省略~

メインスレッドは2つのスレッドが実行され終了されるのを待っているのが確認できます。

joinでエラーハンドリング

スレッドの処理が失敗した場合はどうしたらいいのか気になりました。

spawnのコメントには、

The join handle provides a join method that can be used to join the spawned thread. If the spawned thread panics, join will return an Err containing the argument given to panic!.

と書いてあるので、スレッド内でpanicが発生するとjoinはErrを返すようです。

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 0..10 {
            if i == 5 {
                // エラーを示すためにpanicを起こす
                panic!("スレッドでエラーが発生しました");
            }
            println!("スレッド: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    match handle.join() {
        Ok(_) => println!("スレッドが正常に終了しました"),
        Err(e) => println!("スレッドでエラーが発生しました: {:?}", e),
    }
}
thread '<unnamed>' panicked at src/main.rs:10:17:
スレッドでエラーが発生しました
stack backtrace:
   0: rust_begin_unwind
             at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:595:5
   1: core::panicking::panic_fmt
             at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:67:14
   2: *****::main::{{closure}}
             at ./src/main.rs:10:17
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
スレッド: 0
スレッド: 1
スレッド: 2
スレッド: 3
スレッド: 4
スレッドでエラーが発生しました: Any { .. }

Process finished with exit code 0

スレッドが途中でpanicを返して、メインスレッド側のjoinでハンドリングできることが確認できました。

この記事を書いた人

tkr2f
tkr2f事業開発部 web application engineer
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします