简体   繁体   中英

RwLock in a thread::spawn doesn't release the lock

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();
}

the write access attempt is still blocking the main thread after the spawn thread has been executed and has released the RwLock

This is incorrect. Adding basic debugging output to differentiate them shows that the only println that runs is the one in main .

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 via rw 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:

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!("thread a {}, {}, {}, {}", 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!("thread b {}, {}, {}, {}", my.num1, my.num2, rw[0], rw[1]);
    drop(rw);
    t.join().unwrap();
}

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:

The reason why it did't work correctly is I didn't release lock when the lock in main thread was executed before the one in child thread. I fixed it like below.

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]);
    });
    {
        let mut rw = my.rw.write().unwrap();
        rw[1] = 6;
        println!("main thread {}, {}, {}, {}", my.num1, my.num2, rw[0], rw[1]);
    }
    t.join().unwrap();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM