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

本文へ

フッターへ

お役立ち情報Blog



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

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

今回も「The Rust Programming Language」を読みながら、スレッドについて見ていきたいと思います。
前回の「前回のスレッド編 その1」はこちらからご覧いただけます。

スレッドに名前をつけてみる

前回の最後のエラーで、スレッドがunnamedになっていましたが、どうやらスレッドに名前をつけれるようなので試してみます。

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

thred::spawnは内部的にはthread::Builderを使っているようです。そしてBuilderを使うと名前をつけれるようです。

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

fn main() {
    let builder = thread::Builder::new().name("スレッ丼".into());

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

    match handle.join() {
        Ok(_) => println!("スレッドが正常に終了しました"),
        Err(e) => println!("スレッドでエラーが発生しました: {:?}", e),
    }
}

注意点としては、builder.spawnio::Result<JoinHandle<T>>を返すことです。
とりあえず動作確認したいだけなので、unwrapをつけて処理しておきます。

thread 'スレッ丼' panicked at src/main.rs:12:17:
スレッドでエラーが発生しました
stack backtrace:
スレッド: 0
スレッド: 1
スレッド: 2
スレッド: 3
スレッド: 4
スレッドでエラーが発生しました: Any { .. }
   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:12:17
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Process finished with exit code 0

無事、ふざけた名前のスレッド名が表示されました。

thread.currentを使うと、スレッド内から名前を参照できるらしいので試してみたいと思います。

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

fn main() {
    let builder = thread::Builder::new().name("スレッ丼".into());

    let handle = builder.spawn(|| {
        let handle = thread::current();
        println!("スレッド名: {}", handle.name().unwrap());
        thread::sleep(Duration::from_millis(1));
    }).unwrap();

    match handle.join() {
        Ok(_) => println!("スレッドが正常に終了しました"),
        Err(e) => println!("スレッドでエラーが発生しました: {:?}", e),
    }
}
スレッド名: スレッ丼
スレッドが正常に終了しました

Process finished with exit code 0

問題なく取得できました。

スレッドのスタックサイズを設定してみる

おなじく、thread::Builderを使うことでスレッドのサイズを設定することができるようです。

use std::thread;

fn recursion(n: usize) {
    println!("Depth: {}", n);
    // 再帰呼び出し
    recursion(n + 1);
}

fn main() {
    let builder = thread::Builder::new().stack_size(64 * 1024); // 64KB

    let handler = builder.spawn(|| {
        recursion(1);
    }).unwrap();

    handler.join().unwrap()
}

というわけで、再帰関数を用意し、スタックサイズを64KBにして実行してみます。

warning: function cannot return without recursing
 --> src/main.rs:3:1
  |
3 | fn recursion(n: usize) {
  | ^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
...
6 |     recursion(n + 1);
  |     ---------------- recursive call site
  |
  = help: a `loop` may express intention better if this is on purpose
  = note: `#[warn(unconditional_recursion)]` on by default

warning: `rust-study` (bin "rust-study") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/rust-study`

thread '' has overflowed its stack
fatal runtime error: stack overflow
Depth: 1
Depth: 2
Depth: 3
Depth: 4
Depth: 5
Depth: 6
~~省略~~
Depth: 468

Process finished with exit code 6

予想通りオーバーフローが発生しました。

スタックサイズを小さくしたら再帰の回数も減った。 しかし、極端に小さくした場合は、OSによってスタックの最小値が決まっているらしく、 それ以上小さくならないみたいです。

The actual stack size may be greater than this value if the platform specifies a minimal stack size.

ドキュメントにもそのように書かれています。

あと、再帰する怪しい関数に対してコンパイラが検出してWarningを出してくれてるのが便利だと思いました。

この記事を書いた人

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

FOLLOW US

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