I never worked with asynchronous code (but have exp with multithreading) in Rust. I thought it is mostly the same thing, however I got some problems. Here listing of my sample:
use tokio::{join, sync::RwLock};
use std::sync::Arc;
use tokio::time::{sleep, Duration};
struct MyThing {
n: usize,
}
impl MyThing {
fn new(x: usize) -> MyThing {
MyThing{ n: x }
}
}
async fn rr(v: Arc<RwLock<MyThing>>) {
loop {
let read = v.read().await;
println!("Value : {}", (*read).n);
// sleep(Duration::from_millis(50)).await;
}
}
async fn ww(w: Arc<RwLock<MyThing>>) {
loop {
let mut write = w.write().await;
(*write).n += 2;
println!("Updated value: {}", (*write).n);
sleep(Duration::from_millis(200)).await;
}
}
#[tokio::main]
async fn main() {
let x = Arc::new(RwLock::new(MyThing::new(11)));
let w = x.clone();
let r = x.clone();
let h = tokio::spawn(async move {
ww(w).await
});
let j = tokio::spawn(async move {
rr(r).await
});
join!(h);
join!(j);
}
So i was expecting that I would get 2 async "threads", and while ww
is sleeping I will get ton of readings, but, im getting next output:
Updated value: 13
Value : 13
Updated value: 15
Value : 15
Updated value: 17
Value : 17
Updated value: 19
Value : 19
Updated value: 21
...
So my question is: Is it correct behavior or I just dumb?
(And additional question, if i delete await
in tokio::spawn
after i call function, those functions rr
and ww
aren't called?)
You're sleeping while holding the lock. This bug can be caused in threaded programming too.
Because you don't drop the lock (write
), it is not released, and rr()
cannot acquire a read lock until after the sleep.
Drop the lock before the sleep, and you'll see the expected output:
async fn ww(w: Arc<RwLock<MyThing>>) {
loop {
let mut write = w.write().await;
(*write).n += 2;
println!("Updated value: {}", (*write).n);
drop(write);
sleep(Duration::from_millis(200)).await;
}
}