重い処理を別スレッドで実行する

Recipe ID: rust-013

Tauri(Rust)のメインスレッドは UI の更新なども担当しているため、ここで重い処理(数秒かかる計算など)を行うとアプリがフリーズしたように固まってしまいます。
これを防ぐために、重い処理は「別スレッド」で実行するのが鉄則です。

Cargo.toml の確認 (Rust)

async コマンド内で tokio の機能(tokio::time::sleep など)を直接使うには、Cargo.tomltokio を追加する必要があります。

[dependencies]
tauri = { version = "2.2", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# 以下を追加 (full機能を含めると便利です)
tokio = { version = "1", features = ["full"] }

1. 計算メインの処理 (std::thread)

CPU をぶん回して計算するような処理は、Rust 標準の std::thread::spawn が適しています。

use tauri::{AppHandle, Emitter};

#[tauri::command]
fn start_heavy_calc(app: AppHandle) {
    // スレッド起動(ここから別世界の処理になります)
    std::thread::spawn(move || {
        println!("重い計算を開始...");
        
        // --- 重い処理のシミュレーション ---
        // 実際には画像処理や暗号化計算など
        std::thread::sleep(std::time::Duration::from_secs(5));
        let result = 42; 
        
        // 計算が終わったら、イベントなどでフロントエンドに通知する
        // AppHandle はスレッドセーフなので move して持ち込んでOK
        let _ = app.emit("calc-finished", result);
        
        println!("計算完了!");
    });
    // spawn した瞬間にこの関数自体は終了し、UIスレッドを開放します
}

2. I/O 待ちメインの処理 (async コマンド)

ファイル読み書きやネットワーク通信など、I/O 待ちは async コマンドとして定義するのがベストプラクティスです。
Rust 側で async fn として定義されたコマンドは、Tauri が自動的に非同期ランタイム(Tokio)上で実行するため、メインスレッド(UI)をブロックしません。

// async fn にするだけで、await 中はスレッドが解放されます
#[tauri::command]
async fn fetch_user_data(user_id: u32) -> Result<String, String> {
    println!("ユーザー {} のデータ取得を開始...", user_id);

    // I/O待ちのシミュレーション(3秒非同期スリープ)
    // 注意: std::thread::sleep ではなく tokio::time::sleep を使います
    tokio::time::sleep(std::time::Duration::from_secs(3)).await;
    
    // 実際の HTTP リクエスト例 (reqwestクレートが必要)
    // let resp = reqwest::get(format!("https://api.example.com/users/{}", user_id))
    //    .await
    //    .map_err(|e| e.to_string())?;

    println!("取得完了");
    Ok(format!("User {} data", user_id))
}

フロントエンドからは通常のコマンド同様 invoke で呼び出せます。戻り値は Promise となるため await で待機できます。

どちらを使えばいい?

  • std::thread: 「CPUを常に100%使う」ようなハードな計算処理。
  • async_runtime (Tokio): 「ネットワーク通信」や「タイマー待ち」など、待機時間が長い処理、または async 関数を使いたい場合。

初心者の方は、まずは「重い処理は fn の中で直接やらずに、spawn で逃がす」と覚えておけばOKです。