Passing mutable struct between threads

Hello. I am writing a ticket sales system to show off Rusts "fearless concurrency". My code looks like this (for the thread part)

    let number_of_physical_cores = num_cpus::get();
    let mut children = vec![];
    for _i in 0..number_of_physical_cores {
        let (data, tx) = (Arc::clone(&data), tx.clone());

        children.push(thread::spawn(move || {
            let amount = rand::thread_rng().gen_range(0, 9);
            let mut data = data.lock().unwrap();
            ticket_sales(&mut *data, 0, amount);
            tx.send(data).unwrap();
        }));
    let box_office = rx.recv().unwrap();
    box_office_status(*box_office);
    }
    for child in children {
        let _ = child.join();
    }

I am passing around a struct which is serving as a "box office" that holds several types of ticket structs in a vector. It also has a balance field. The ticket_sales function really just subtracts x number of tickets and adds money to the balance of the box office. It all seems to be working fine until I add a channel to pass the box office information out of the threads. Is there a way for me to do this without importing unsafe libraries? (For clarity the "data" field is the box_office struct)

You are trying to send a lock guard over a channel, try sending the data itself or making a separate type that represents the data and sending it over the channel.

Thank you for the swift answer.
Do you mean unlocking and dereferencing the data, and then passing it? I get the "cannot move out of borrowed content" error.

The error message tells you that you are trying to take something that you do not own. Rust does not permit that kind of theft. :smiley: (At least that's one way of looking at it.)

You can send over the data that you need to send, for example you could send over how many tickets you sold. Anything other than sending over borrowed values should be fine.

In general, objects that are behind a Mutex cannot be sent to another thread without either (a) removing the item from the Mutex or (b) making a copy/clone of it.

Example of the former (a):

use std::sync::{Arc, Mutex};
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;

#[derive(Debug)]
struct Data;

let (tx, rx): (Sender<Data>, Receiver<Data>) = mpsc::channel();
let m: Arc<Mutex<Option<Data>>> = Arc::new(Mutex::new(Some(Data)));
thread::spawn(move || {
    let data = m.lock().unwrap().take().unwrap();
    tx.send(data).unwrap();
});

println!("{:?}", rx.recv().unwrap());

Thank you. That makes perfect sense.

Thanks @Fylwind, I will try this also.

For those interested, I solved it by implementing clone, and then sending it out of the threads.

    while true {
        let (tx, rx) = channel();
        let data = Arc::new(Mutex::new(box_office.clone()));

        let number_of_physical_cores = num_cpus::get();
        let mut children = vec![];
        for _i in 0..number_of_physical_cores {
            let (data, tx) = (Arc::clone(&data), tx.clone());

            children.push(thread::spawn(move || {
                let amount = rand::thread_rng().gen_range(0, 9);
                let mut data = data.lock().unwrap();
                ticket_sales(data.deref_mut(), 0, amount);
                tx.send(data.deref().clone());
            }));
        }
        for child in children {
            let _ = child.join();
        }
        box_office = rx.recv().unwrap();
        if box_office_status(&box_office) <= 0 {
            break;
        }
    }

(edit for code error)

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.