Threads logic help needed

Hi,
I am trying to write a program which calculates number of directories under specific path while at the same time displaying number of found directories. Could you please help me fix it ?
Thank you

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use walkdir::WalkDir;

fn main() {
    let total_dirs = Arc::new(Mutex::new(0));

    let total_dirs_clone = Arc::clone(&total_dirs);

    thread::spawn(move || {
        let mut num_dirs = total_dirs_clone.lock().unwrap();

        for entry in WalkDir::new("/").into_iter().filter_map(Result::ok) {
            if entry.file_type().is_dir() {
                *num_dirs += 1;
            }
            thread::sleep(Duration::from_millis(1));
        }
    });

    loop {
        let total_dirs_clone = Arc::clone(&total_dirs);
        let num_dirs = total_dirs_clone.lock().unwrap();
        println!("Total directories found {}", num_dirs);
        thread::sleep(Duration::from_millis(200));
    }
}

You're not releasing the locks when they aren't used, so the first thread to get hold on the lock holds it until the end of work (for walker thread) or until the end of current iteration (for printing thread). Lock the mutex right before using the value and drop the lock immediately afterwards, and all should work.

Thank you @Cerber-Ursi, your comment was really helpful.
Code runs now, but I don't understand why it's so slow.

This runs almost instantly:

use std::thread;
use std::time::{Duration};
use walkdir::WalkDir;

fn main() {
    thread::spawn(move || {
        let mut found_dirs = 0;
        for entry in WalkDir::new("/Users/User/")
            .into_iter()
            .filter_map(Result::ok)
        {
            if entry.file_type().is_dir() {
                found_dirs += 1;
                println!("{}", found_dirs);
            }
        }
    });

    loop {
        println!("Sleeping for 1 sec");
        thread::sleep(Duration::from_millis(1000));
    }
}

While code using mutexes is 100s times slower:

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use walkdir::WalkDir;

fn main() {
    let total_dirs = Arc::new(Mutex::new(0));

    let total_dirs_clone = Arc::clone(&total_dirs);

    thread::spawn(move || {
        for entry in WalkDir::new("/Users/User/")
            .into_iter()
            .filter_map(Result::ok)
        {
            if entry.file_type().is_dir() {
                let mut num_dirs = total_dirs_clone.lock().unwrap();
                *num_dirs += 1;
            }
        }
    });

    loop {
        let total_dirs_clone = Arc::clone(&total_dirs);
        let num_dirs = total_dirs_clone.lock().unwrap();
        println!("Total directories found {}", num_dirs);
        thread::sleep(Duration::from_millis(1000));
    }
}

Could you please point me to where the problem could be ?

Your main thread sleeps with the lock held, giving the thread that's counting the directories only a brief shot at the lock once every second.

Try:

loop {
       {
         let total_dirs_clone = Arc::clone(&total_dirs);
         let num_dirs = total_dirs_clone.lock().unwrap();
         println!("Total directories found {}", num_dirs);
       }
       thread::sleep(Duration::from_millis(1000));
}

Thanks so much @godmar, what a subtle difference !
Dynamically typed languages spoiled me :slight_smile:

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.