[Rust] tokio: tokio-console
tokio-console์ tokio ์ ์ฉ ๋๋ฒ๊น
๋๊ตฌ ํ๊ฒฝ์ด๋ค.
๋ณต์กํ ๋์์ฑ ํ๊ฒฝ์์์ ๋ฆฌ์์ค ์ถ์ , ๋๋ฒ๊น
๋ฑ์ ์ฉ์ดํ๊ฒ ํด์ค๋ค.
console CLI ์ค์น
๋จผ์ cargo install์ ํตํด ๋๊ตฌ๋ฅผ ์ค์นํ๋ค.
cargo install --locked tokio-console

๊ทธ๋ฆฌ๊ณ ์คํํด๋ณด๋ฉด
tokio-console
๋ณ ์๋ฏธ์๋๊ฑด ์๋ฐ ๊ฒ์ด๋ค.
์ด๊ฑด ๊ธฐ๋ณธ์ ์ผ๋ก client ๋๊ตฌ๊ณ , grpc๋ฅผ ํตํด ํธ๋ ์ด์ฑ์ ์ผ์ค ์ฑ ์๋ฒ์ ํต์ ํ๋ฉฐ ๊ธฐ๋ก์ ๋ณด์ฌ์ค๋ค.
์ด์ ์ฑ์ ๊ตฌ์ฑํด๋ณด์.
์ฑ ๊ตฌ์ฑ
tokio ์ฑ์ด๋๊น tokio๋ ๋น์ฐํ ์์ด์ผํ๊ณ , console-subscriber ์ข ์์ฑ์ด ์ถ๊ฐ๋ก ํ์ํ๋ค.
tokio์ ์คํ์ ๊ธฐ๋ฅ์ธ tracing๋ ํ์ํ๋ค. ๋ฃ์ด์ค๋ค.
์์ ์ฝ๋๋ค.
use std::{error::Error, thread};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
console_subscriber::init();
loop {
tokio::spawn(async {
std::thread::sleep(std::time::Duration::from_secs(10));
println!("done");
});
thread::sleep(std::time::Duration::from_secs(1));
}
}
๋ณ๊ฑด ์๋ค.
๋ฃจํ๋๋ฉด์ ๋๋ฆฟ๋๋ฆฟํ๊ฒ ํ์คํฌ๋ฅผ ํ๋์ฉ ๋์ฐ๊ณ , 10์ด๊ฐ ์ง๋๋ฉด ๋ก๊ทธ์ฐ๊ณ ์ฃฝ๋๊ฒ ๋ค๋ค.
๋ก์ง๊ณผ ๋ณ๊ฐ๋ก console_subscriber::init(); ์ด๊ธฐ ์ ์ธ ํ๋ฒ๋ง ํด์ฃผ๋ฉด tracing ์๋ฒ๊ฐ ๋ฌ๋ค. ์ ๊ฒ ์ค์ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์คํ์ ํด์ค๋ค.
RUSTFLAGS="--cfg tokio_unstable" cargo run
์ ๋ ๊ฒ ์๋ฒ๊ฐ ๋จ๊ณ ๋์
tokio-console์ ๋ค์ ์ผ๋ณด๋ฉด

์ด๋ฒ์๋ ์ฐ๊ฒฐ์ด ๋ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ ๋ชจ๋ ํ์คํฌ์ ํํฉ์ ์ค์๊ฐ์ผ๋ก ๋ณผ ์ ์๋ค.
์คํ์ค์ธ ํ์คํฌ๋ โถ, ์ผ์์ ์ง ์ํ(sleep)๋ โธ. ์ ์ง๋ ํ์คํฌ๋ โน ๋ก ํ์๋๋ค.
๊ฐ๊ฐ์ ํ์คํฌ๊ฐ ๋ฌ ์ค๋ ๋จ๊ณ , ์ด๋ฐ์ ๋ฐ ์ ๋ณด๋ค์ด ๊ฝค ํ์๋๋ค.
์ํฐ์ณ์ ํ์คํฌ ์์ธ๋ก ๋ค์ด๊ฐ๋ฉด ์กฐ๊ธ ๋ ๋ง์ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์๋ค.
๋ค๋ฅธ ์์ : long sleep
์๋๋ ๋น์ ์์ ์ธ ์ ๋๋ก sleep์ ๊ฑธ๊ณ ์๋ฌด๊ฒ๋ ํ์ง ์๋ ํ์คํฌ๋ฅผ ํ๋ ๋ง๋ ๋ค.
use std::{error::Error, time::Duration};
use tokio::task::yield_now;
async fn long_sleeps(inc: u64) {
let millis = inc;
loop {
std::thread::sleep(Duration::from_millis(millis));
yield_now().await;
}
}
async fn sleep_forever(inc: u64) {
let millis = inc;
loop {
std::thread::sleep(Duration::from_millis(millis));
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
console_subscriber::init();
let long_sleeps = tokio::task::Builder::new()
.name("long-sleeps")
.spawn(long_sleeps(5000))
.unwrap();
let sleep_forever = tokio::task::Builder::new()
.name("sleep-forever")
.spawn(sleep_forever(5000))
.unwrap();
match (long_sleeps.await, sleep_forever.await) {
(Ok(_), Ok(_)) => println!("Success"),
(_, _) => println!("Error awaiting tasks."),
}
tokio::time::sleep(Duration::from_millis(200)).await;
Ok(())
}
long_sleeps๋ ๋ฌดํ์ผ๋ก ๋๊ธด ํ์ง๋ง, yield๋ฅผ ํตํด ์๋ณด๋ฅผ ํ๋ ๋ฐ๋ฉด sleep_forever์ ๋์ฑ ์์ด ๋ฃจํ๋ฅผ ๋๋ฉฐ ๋ฆฌ์์ค๋ฅผ ์ ์ ํ๋ค.

๊ทธ๋ผ ์ด๋ฐ์์ผ๋ก ๊ฒฝ๊ณ ๊ฐ ํญ์ ๋ฌ๋ค.
๋ฐ๋๋ฝ์ด ๋ฐ์ํด๋ ์ด๋ฐ ๋๋์ผ๋ก ๋จ๋๋ผ
๋ค๋ฅธ ์์ : mutex deadlock
์๋๋ Mutex๋ฅผ ํตํด ๋ฐ๋๋ฝ์ ์ ๋ฐ์ํค๋ ์์ ์ฝ๋๋ค.
ํฌ๊ฒ ํน๋ณํ ๊ฒ์ ์๋ค.
use std::sync::{Arc, Mutex};
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
console_subscriber::init();
let a = Arc::new(Mutex::new(0));
let b = Arc::new(Mutex::new(0));
{
let a = a.clone();
let b = b.clone();
tokio::task::Builder::default()
.name("A")
.spawn(async move {
let mut a_num = a.lock().unwrap();
*a_num += 1;
let delay = std::time::Duration::from_millis(1000);
std::thread::sleep(delay);
let mut b_num = b.lock().unwrap();
*b_num += 1;
})
.unwrap();
}
{
let a = a.clone();
let b = b.clone();
tokio::task::Builder::default()
.name("B")
.spawn(async move {
let mut b_num = b.lock().unwrap();
*b_num += 1;
let delay = std::time::Duration::from_millis(1000);
let mut a_num = a.lock().unwrap();
*a_num += 1;
})
.unwrap();
}
Ok(())
}
์๋ก์๋ก lock์ ๊ฑธ๋ฉด์ ๋จนํต๋๋ ์ ํ์ ์ธ ๋ฐ๋๋ฝ ์ฝ๋๋ค.
์ ๊ฑธ ๊ด์ธกํ๋ฉด
์ด๋ฐ ๋๋์ผ๋ก ๋ฌ๋ค.