동시성 연습 문서의 문제 4 풀이 코드다.
//! 동시성 연습 p4: 멈춤 깃발과 진행률. 어느 Ordering이면 충분한지 고르는 문제의 답을 돌려 본다.
//! 멈춤 깃발과 진행률은 Relaxed로 충분하다(다른 데이터를 깃발로 넘기지 않고, 결과는 join으로 받는다).
//! 깃발로 데이터를 넘기는 판은 Release/Acquire가 필요하다.
//! 주의: 이 실행이 통과해도 순서가 옳다는 증명은 아니다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/4.연습문제/35_concurrency-practice_p4.rs"
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::thread;
use std::time::Duration;
/// 일꾼 넷이 stop이 켜질 때까지 일한다. 각자 처리한 개수를 join으로 돌려준다.
fn run_until_stopped() -> (u64, u64) {
let stop = AtomicBool::new(false);
let progress = AtomicU64::new(0);
let per_worker: Vec<u64> = thread::scope(|s| {
let hs: Vec<_> = (0..4)
.map(|_| {
s.spawn(|| {
let mut done = 0u64;
while !stop.load(Ordering::Relaxed) {
done += 1;
progress.fetch_add(1, Ordering::Relaxed);
if done.is_multiple_of(1024) {
thread::yield_now();
}
}
done
})
})
.collect();
// 진행률이 1,000을 넘을 때까지 기다린 뒤 멈춘다
while progress.load(Ordering::Relaxed) < 1_000 {
thread::sleep(Duration::from_millis(1));
}
stop.store(true, Ordering::Relaxed);
hs.into_iter().map(|h| h.join().unwrap()).collect()
});
// scope가 끝났으므로(join) 일꾼들의 모든 쓰기가 보인다
(per_worker.iter().sum(), progress.load(Ordering::Relaxed))
}
/// 깃발로 결과를 넘기는 판: 결과는 보통 변수가 아니라 원자 변수 둘이지만,
/// 두 변수의 순서를 지켜야 하므로 Release/Acquire가 필요하다.
fn handoff() -> usize {
let result = AtomicUsize::new(0);
let ready = AtomicBool::new(false);
thread::scope(|s| {
s.spawn(|| {
result.store(42, Ordering::Relaxed);
ready.store(true, Ordering::Release);
});
while !ready.load(Ordering::Acquire) {
std::hint::spin_loop();
}
result.load(Ordering::Relaxed)
})
}
fn main() {
let (sum, progress) = run_until_stopped();
assert_eq!(sum, progress);
assert!(progress >= 1_000);
println!("stop flag (Relaxed): workers stopped, sum of per-worker counts == progress counter");
for _ in 0..1_000 {
assert_eq!(handoff(), 42);
}
println!("Release/Acquire handoff: 1000/1000 saw 42");
println!("ALL CHECKS PASSED");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handoff_once() {
assert_eq!(handoff(), 42);
}
}