He creado una versión simplificada de mi problema a continuación, tengo una estructura Bag y una estructura Item. Quiero generar 10 subprocesos que ejecuten el método item_action de Bag en cada item en un item_list, e imprimir una declaración si los atributos de ambos elementos están en el attributes de la bolsa.

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

#[derive(Clone, Debug)]
struct Bag{
    attributes: Arc<Mutex<Vec<usize>>>
}

impl Bag {
    fn new(n: usize) -> Self {
        let mut v = Vec::with_capacity(n);
        for _ in 0..n {
            v.push(0);
        }

        Bag{
            attributes:Arc::new(Mutex::new(v)),
        }
}

    fn item_action(&self, item_attr1: usize, item_attr2: usize) -> Result<(),()> {

        if self.attributes.lock().unwrap().contains(&item_attr1) ||
        self.attributes.lock().unwrap().contains(&item_attr2) {

            println!("Item attributes {} and {} are in Bag attribute list!", item_attr1, item_attr2);
            Ok(())
        } else {
            Err(())
        }
    }
}

#[derive(Clone, Debug)]
struct Item{
    item_attr1: usize,
    item_attr2: usize,
}

impl Item{
    pub fn new(item_attr1: usize, item_attr2: usize) -> Self {
        Item{
            item_attr1: item_attr1,
            item_attr2: item_attr2
        }
    }
}

fn main() { 
    let mut item_list: Vec<Item> = Vec::new();
    for i in 0..10 { 
        item_list.push(Item::new(i, (i+1)%10));
    }

    let bag: Bag= Bag::new(10); //create 10 attributes

    let mut handles = Vec::with_capacity(10);

    for x in 0..10 {
        let bag2 = bag.clone();
        let item_list2= item_list.clone();

        handles.push(
            thread::spawn(move || {
                bag2.item_action(item_list2[x].item_attr1, item_list2[x].item_attr2);
            })
        )
    }

    for h in handles {
        println!("Here");
        h.join().unwrap();
    }
}

Cuando se ejecuta, solo tengo una línea y el programa simplemente se detiene allí sin regresar.

Item attributes 0 and 1 are in Bag attribute list!

¿Puedo saber qué salió mal? Consulta el código en Zona de juegos

Actualizado :

Con la sugerencia de @loganfsmyth, el programa puede regresar ahora ... pero todavía solo imprime 1 línea como arriba. Espero que imprima 10 porque mi item_list tiene 10 elementos. No estoy seguro de si la lógica de mi hilo es correcta.

Agregué println!("Here"); al llamar a join todos los hilos. Y puedo ver que Here se imprime 10 veces, pero no el registro real de item_action

0
hydradon 24 oct. 2019 a las 01:03

1 respuesta

La mejor respuesta

Creo que esto se debe a que Rust no está ejecutando su

if self.attributes.lock().unwrap().contains(&item_attr1) ||
    self.attributes.lock().unwrap().contains(&item_attr2) {

Expresión en el orden esperado. El orden de evaluación de las subexpresiones en Rust no está definido actualmente. Lo que parece estar sucediendo es que esencialmente terminas con

const condition = {
  let lock1 = self.attributes.lock().unwrap();
  let lock2 = self.attributes.lock().unwrap();
  lock1.contains(&item_attr1) || lock2.contains(&item_attr2)
};
if condition {

Lo que está causando que su código se bloquee.

En su lugar, debería escribir:

let attributes = self.attributes.lock().unwrap();
if attributes.contains(&item_attr1) ||
   attributes.contains(&item_attr2) {

De modo que solo haya una cerradura.

Su código también funcionaría tal cual si usara un {{X0} } o ReentrantMutex en su lugar de un Mutex ya que permiten que el mismo hilo tenga múltiples referencias inmutables a los datos.

1
loganfsmyth 24 oct. 2019 a las 00:59