Rust: ошибка параллелизма, программа зависает после первого потока - PullRequest
0 голосов
/ 24 октября 2019

Я создал упрощенную версию своей задачи ниже, у меня есть Bag структура и Item структура. Я хочу создать 10 потоков, выполняющих метод item_action из Bag на каждом item в item_list, и распечатать инструкцию, если оба атрибута элемента находятся в attributes.

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();
    }
}

При запуске у меня только одна строка, и программа просто останавливается там, не возвращаясь.

Item attributes 0 and 1 are in Bag attribute list!

Могу я узнать, что пошло не так? Пожалуйста, смотрите код в Детская площадка

Обновлено :

С предложением @loganfsmyth, программа может вернуться сейчас ... но все еще только печатает 1линия, как указано выше. Я ожидаю, что это напечатает 10, потому что у моего item_list есть 10 пунктов. Не уверен, что логика моего потока верна.

Я добавил println!("Here"); при вызове join всех потоков. И я вижу, что Here напечатано 10 раз, просто не фактический журнал от item_action

1 Ответ

1 голос
/ 24 октября 2019

Я полагаю, это потому, что Rust не запускает ваше

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

выражение в том порядке, в котором вы ожидаете. Порядок вычисления подвыражений в Rust в настоящее время не определен. Похоже, происходит то, что вы в конечном итоге получаете

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

, что приводит к тупику вашего кода.

Вместо этого вы должны написать:

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

такчто есть только одна блокировка.

Ваш код также будет работать как есть, если вы используете RwLock или ReentrantMutex вместо Mutexпоскольку они позволяют одному и тому же потоку иметь несколько неизменных ссылок на данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...