I'm not sure why it hangs on my.rw.write();
.
If you uncomment the sleep
call, it works fine. This means that the write access attempt is still blocking the main thread after the spawn thread has been executed and has released the RwLock
. I think it must be fine in theory. Where am I wrong?
use std::sync::Arc;
use std::sync::RwLock;
use std::{thread, time};
struct MySt {
num1: i64,
num2: i64,
rw: RwLock<Vec<i64>>,
}
fn main() {
let my = Arc::new(MySt {
num1: 32,
num2: 64,
rw: RwLock::new(vec![1, 2, 3]),
});
let my2 = my.clone();
let t = thread::spawn(move || {
let mut rw = my2.rw.write().unwrap();
rw[0] = 5;
println!("child thread {}, {}, {}, {}", my2.num1, my2.num2, rw[0], rw[1]);
});
//thread::sleep(time::Duration::from_millis(1000));
let mut rw = my.rw.write().unwrap();
rw[1] = 6;
println!("main thread {}, {}, {}, {}", my.num1, my.num2, rw[0], rw[1]);
t.join().unwrap();
}
This is incorrect. Adding basic debugging output to differentiate them shows that the only
println
that runs is the one inmain
.There's no guarantee which thread will execute first after the child thread is spawned. However, the main thread is likely to continue running since it's already running. If so it will lock the
RwLock
viarw
and hold the lock until the end of the function. However, before the end of the function, the main thread will block waiting for the child thread to join. The child thread cannot finish because it must acquire the write lock first. You have created a classic deadlock.To solve it, explicitly unlock the lock using
drop
or add scopes to constrain the lock's lifetime:Adding the
sleep
is likely to cause the child thread to execute first. It will then acquire the lock and exit, allowing the main thread to proceed.See also: