元ネタ
@NemesisさんのJavaで湯婆婆を実装してみるに便乗してみました。
のんびり書いてる間に先を越された。
でもせっかくだから投稿しちゃう。
コード
use std::sync::{Arc, Mutex, Barrier};
use std::thread;
use rand::Rng;
fn main() {
let mut handles = Vec::with_capacity(3);
let name = Arc::new(Mutex::new(String::new()));
let barrier = Arc::new(Barrier::new(3));
handles.push(thread::spawn({
let name = name.clone();
let barrier = barrier.clone();
move || {
{
let stdin = std::io::stdin();
let mut locked = name.lock().unwrap();
println!("湯婆婆A:契約書だよ。そこに名前を書きな。");
let _ = stdin.read_line(&mut *locked);
}
barrier.wait();
}
}));
handles.push(thread::spawn({
let name = name.clone();
let barrier = barrier.clone();
move || {
barrier.wait();
let mut locked = name.lock().unwrap();
println!("湯婆婆B:フン。{}というのかい。贅沢な名だねぇ。", locked.trim());
let n = rand::thread_rng().gen_range(0, locked.trim().chars().count());
*locked = locked.trim().chars().nth(n).unwrap().to_string();
}
}));
handles.push(thread::spawn({
let name = name.clone();
let barrier = barrier.clone();
move || {
barrier.wait();
println!("湯婆婆C:今からお前の名前は{0}だ。いいかい、{0}だよ。分かったら返事をするんだ、{0}!!", name.lock().unwrap().trim());
}
}));
handles.into_iter().for_each(|h| { let _ = h.join(); });
}
ポイズン湯婆婆
空白のままにしますと、あるスレッドがMutexをロックしたまま終了しますので、湯婆婆Cはポイズンエラーが発生します。ポイズン湯婆婆。
もちろん湯婆婆Bはクラッシュ湯婆婆してます。
連帯感
おわりに
Rustは並列処理を比較的気持ちよくかけます。
しかし、やっぱり並列処理の基本的な考えをおろそかにしていると、デバックしにくかったり、実行タイミングが管理しきれなくなったりします。
それでも、Rustは並列処理を書きやすいです。
メモリ競合を抑えるためのMutex
やタイミングを合わせるためのbarrier
、デバックに関してはstd::io::Error
をnew
すれば手軽にデバック用のメッセージを飛ばせる(要件に合わせて自作Error型を作ったほうがいい気はするけど。。)。
Rustはいいですよぉ。とくにC++を学んだ方にはおすすめします。C++経験者であればすんなり馴染めることでしょう。