Rust:并发错误,程序在第一个线程后挂起



我在下面创建了问题的简化版本,我有一个Bag结构和Item结构。我想在item_list中的每个item上从Bag生成10个执行item_action方法的线程,如果两个项目的属性都在Bag的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!

我可以知道出了什么问题吗?请参阅游乐场中的代码

更新

根据@loganofsmyth的建议,该程序现在可以返回。。。但是仍然如上所述仅打印1行。我希望它能打印10个,因为我的item_list有10个项目。不确定我的线程逻辑是否正确。

我在调用join所有线程时添加了println!("Here");。我可以看到Here被打印了10次,只是不是item_action的实际日志

我相信这是因为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) {

从而只有一个锁。

如果您使用RwLockReentrantMutex而不是Mutex,那么您的代码也将按原样工作,因为它们允许同一线程对数据有多个不可变的引用。

相关内容

最新更新