Giter Site home page Giter Site logo

Comments (15)

jdm avatar jdm commented on May 5, 2024 29

Yep, that's equivalent to this minimal solution that I had in mind:

use std::sync::{Arc, Mutex};
use std::thread;

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep_ms(500);
            status_shared.lock().unwrap().jobs_completed += 1;
        }
    });
    while status.lock().unwrap().jobs_completed < 10 {
        println!("waiting...");
        thread::sleep_ms(1000);
    }
}

from rustlings.

Wieke avatar Wieke commented on May 5, 2024 2

Anticipating a deadlock due to the while loop I opted to use a normal loop instead. The point being that I was unsure how the while loop would handle the lock, whether it would be released before the content of the loop was executed (though apparently it does so my approach was a bit more convoluted than necessary). (Also this way I could print the number of completed jobs.)

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            let mut x = status_shared.lock().unwrap();
            x.jobs_completed += 1;
        }
    });
    loop {
        {
            let x = status.lock().unwrap();
            println!("{} done", x.jobs_completed);
            if x.jobs_completed >= 10 { break; }
        } // Lock going out of scope
        thread::sleep(Duration::from_millis(500));
    }
}

from rustlings.

zphixon avatar zphixon commented on May 5, 2024 2

This syntax was called a weird hack but I think it's kind of cute here.

while {
    let handle = status.lock().unwrap();
    println!("{} done", handle.jobs_completed);
    handle.jobs_completed < 10
} {
    println!("waiting... ");
    thread::sleep(Duration::from_millis(500));
}

from rustlings.

sp-1234 avatar sp-1234 commented on May 5, 2024 1

neither of the above did work for me — I was always deadlocking.
what did work is this:

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            status_shared.lock().unwrap().jobs_completed += 1;
        }
    });
    fn jobs_completed(s: &Arc<Mutex<JobStatus>>) -> u32 {
        s.lock().unwrap().jobs_completed
    };
    while jobs_completed(&status) < 10 {
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

So I had to put the main thread's lock into a function.

from rustlings.

superhawk610 avatar superhawk610 commented on May 5, 2024 1

Any ideas why this only prints waiting... once?

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

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = Arc::clone(&status);

    thread::spawn(move || {
        for _ in 0..10 {
            let mut stat = status_shared.lock().unwrap();
            stat.jobs_completed += 1;
            println!("set completed jobs to {}", stat.jobs_completed);

            thread::sleep(Duration::from_millis(250));
        }
    });

    while status.lock().unwrap().jobs_completed < 10 {
        println!("waiting... ");

        thread::sleep(Duration::from_millis(500));
    }
}

Output:

waiting... 
set completed jobs to 1
set completed jobs to 2
set completed jobs to 3
set completed jobs to 4
set completed jobs to 5
set completed jobs to 6
set completed jobs to 7
set completed jobs to 8
set completed jobs to 9
set completed jobs to 10

EDIT: Just figured it out! Moving the thread::sleep() to the end of the spawned thread will cause the spawned thread to always hold the lock on the mutex and never yield it back to the main thread, so the while loop will only run once!

from rustlings.

jdm avatar jdm commented on May 5, 2024 1

@cyyyu That is a valid program, but by using join() to block until the thread has finished running it avoids the main problem of the exercise, which is to experiment with sharing memory between two threads that are actively running simultaneously.

from rustlings.

jdm avatar jdm commented on May 5, 2024 1

AtomicU32 is a fine solution.

from rustlings.

carols10cents avatar carols10cents commented on May 5, 2024

Ok, so, real talk, your exercises are not straightforward for me, I am a Ruby developer who has not spawned threads in many moons :) So before I write hints for this, I want to check that I got a solution along the lines of what you were thinking for this one:

This was really great for me actually, because it put me more in the shoes of people who are newer to Rust than I am doing the exercises I wrote, which will help me write better hints, and I learned a lot working on this, which validates my hope that these are useful to people :)

// Make this compile!

use std::sync::{Arc, Mutex};
use std::thread;

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep_ms(250);
            let mut status_shared = status_shared.lock().unwrap();
            status_shared.jobs_completed += 1;
        }
    });
    let status_check = status.clone();
    let mut jobs_completed = {
        let s = status_check.lock().unwrap();
        s.jobs_completed
    };
    while jobs_completed < 10 {
        println!("waiting... {}", jobs_completed);
        thread::sleep_ms(500);
        jobs_completed = {
            let s = status_check.lock().unwrap();
            s.jobs_completed
        };
    }
}

from rustlings.

carols10cents avatar carols10cents commented on May 5, 2024

I bumped the timeouts down because otherwise you hit the playground timeout limits :)

from rustlings.

ehiggs avatar ehiggs commented on May 5, 2024

The first hint gives a reference to a redirect page. In the 2nd version of the book, it starts talking about channels before shared state so I solved it using channels as:

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            tx.send(1).unwrap();
        }
    });
    let mut received = 0;
    while received < 10 {
        received += rx.try_iter().sum::<i32>();
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

... which is obviously not straight forward given the initial setup. But a nice little exercise!

from rustlings.

pickfire avatar pickfire commented on May 5, 2024

I got a solution similar to @jdm's solution except that status_shared was put into a variable.

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

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            let mut status_shared = status_shared.lock().unwrap();
            status_shared.jobs_completed += 1;
        }
    });

    while status.lock().unwrap().jobs_completed < 10 {
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

from rustlings.

Gadzev avatar Gadzev commented on May 5, 2024

This worked for me:

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

struct JobStatus {
    jobs_completed: Mutex<i32>,
}

fn main() {
    let status = Arc::new(JobStatus { jobs_completed: Mutex::new(0) });
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            let mut job_c = status_shared.jobs_completed.lock().unwrap();
            *job_c += 1;
        }
    });
    while *status.jobs_completed.lock().unwrap() < 10 {
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

from rustlings.

cyyyu avatar cyyyu commented on May 5, 2024

what I just learned from The Rust Programming Language is that you can actually make it even simpler:

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

struct JobStatus {
    jobs_completed: u32,
}

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = Arc::clone(&status);
    let handle = thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            let mut jobstatus = status_shared.lock().unwrap();
            jobstatus.jobs_completed += 1;
        }
    });

    handle.join().unwrap();

    println!("Jobs completed: {}", status.lock().unwrap().jobs_completed);
}

does my program make sense?

from rustlings.

cyyyu avatar cyyyu commented on May 5, 2024

@jdm I forgot the purpose 😅thanks for explanation.

from rustlings.

EinDev avatar EinDev commented on May 5, 2024

Is there any reason i shouldn't use AtomicU32 for this purpose?
According to this performance comparison i found it should also be faster than by using Mutex.
Of course you need to specify the byte ordering which may introduce confusion for beginners.
This is my (working) program:

use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::sync::atomic::{AtomicU32, Ordering};

struct JobStatus {
    jobs_completed: AtomicU32,
}

fn main() {
    let status = Arc::new(JobStatus { jobs_completed: AtomicU32::new(0) });
    let status_clone = Arc::clone(&status);
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            status_clone.jobs_completed.fetch_add(1, Ordering::Relaxed);
        }
    });
    while status.jobs_completed.load(Ordering::Relaxed) < 10 {
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

from rustlings.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.