When does thread actually switches

My goal : I just wanted to understand how to use shared state concurrency. Here, the process switches thread as soon as i update that data inside RwLock and when i use RwLock's read() method inside the main thread loop.

use std::{
    os::unix::thread,
    sync::{Arc, Mutex, RwLock},
    time::Duration,
};

// Entry of the main thread, we are gonna draw the TUI

struct AppState {
    currentValue: i32,
}

fn main() {
    // Shared state
    let sharedState = Arc::new(RwLock::new(AppState { currentValue: 0 }));
    let threadData = Arc::clone(&sharedState);

    let handle = std::thread::spawn(move || {
        working_thread_main(threadData);
    });

    loop {
        let readlock = sharedState.read().expect("Couldn't get read lock");
        println!("{}", readlock.currentValue);
    }
}

// Entry of the working thread, we are gonna work here
fn working_thread_main(
    shared_state: Arc<RwLock<AppState>>,
) -> Result<(), Box<dyn std::error::Error>> {
    let body = async move {
        loop {
            shared_state
                .write()
                .expect("Couldn't get write lock")
                .currentValue += 1;
            println!("HEre");
        }
    };

    tokio::runtime::Runtime::new()?.block_on(body);
    Ok(())
}

But the process doesn't switch the thread and work when i just get the read lock, outside of loop block in main thread.

use std::{
    os::unix::thread,
    sync::{Arc, Mutex, RwLock},
    time::Duration,
};

// Entry of the main thread, we are gonna draw the TUI

struct AppState {
    currentValue: i32,
}

fn main() {
    // Shared state
    let sharedState = Arc::new(RwLock::new(AppState { currentValue: 0 }));
    let threadData = Arc::clone(&sharedState);

    let handle = std::thread::spawn(move || {
        working_thread_main(threadData);
    });
     let readlock = sharedState.read().expect("Couldn't get read lock");
    loop {
        println!("{}", readlock.currentValue);
    }
}

// Entry of the working thread, we are gonna work here
fn working_thread_main(
    shared_state: Arc<RwLock<AppState>>,
) -> Result<(), Box<dyn std::error::Error>> {
    let body = async move {
        loop {
            shared_state
                .write()
                .expect("Couldn't get write lock")
                .currentValue += 1;
            println!("HEre");
        }
    };

    tokio::runtime::Runtime::new()?.block_on(body);
    Ok(())
}

FYI : I'm noob at rust

Can someone help me understand why the first one switches thread, and the second one doesn't?

Is the thread only switched when i drop the lock that i acquired? As in the first case or how does it work, i dont get it

Threads can run at the same time (that's what multiple CPU cores are for) or switch at any time at all. Your program isn't affected by thread scheduling behavior very much, though.

What you are seeing is that while you hold the read lock the thread trying to acquire the write lock is blocked — cannot make progress.

In the first program the reader loop repeatedly releases and reacquires the read lock, so the writer thread has a chance to acquire a write lock. In the second program, there is never such a chance because the infinite loop runs with the lock held.

5 Likes

Thank you soooo much man, it totally makes sense finally!.

1 Like

Note that your use of a tokio runtime seems questionable and unnecessary. Unnecessary because your async block doesn't contain any awaits so you don't need any async runtime at all, and questionable because it contains an infinite loop without await, which means that this is blocking code that doesn't play nicely with the Tokio runtime (it blocks its worker thread, which might be the whole runtime of it's single-threaded).

1 Like

i was using tokio for my other work.......the above code was just for showing that problem

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.