Concurrency 无法识别死锁

Concurrency 无法识别死锁,concurrency,rust,Concurrency,Rust,我能做的最简单的例子是(): 使用横梁::队列::SegQueue; 使用停车场:{Mutex,MutexGuard}; 使用std::sync::Arc; 使用std::线程; 使用std::time::Duration; 结构就绪信号{ 客户:Arc, 第节:使用, } 结构客户端{ id:使用, 档案:Vec,, } fn main(){ 设max_clients=10; 让文件大小=1000; 让req_q=Arc::new(SegQueue::new()); 设v:Vec=Vec::n

我能做的最简单的例子是():

使用横梁::队列::SegQueue;
使用停车场:{Mutex,MutexGuard};
使用std::sync::Arc;
使用std::线程;
使用std::time::Duration;
结构就绪信号{
客户:Arc,
第节:使用,
}
结构客户端{
id:使用,
档案:Vec,,
}
fn main(){
设max_clients=10;
让文件大小=1000;
让req_q=Arc::new(SegQueue::new());
设v:Vec=Vec::new();
让client_arr=Arc::new(Mutex::new(v));
让client_arr_c=Arc::clone(&client_arr);
线程::生成(移动| |循环{
线程::睡眠(持续时间::从_毫秒(10));
让mut display=String::new();
让client_arr_c=client_arr_c.lock();
对于客户端中的i_arr_c.iter(){
让client=i.lock();
让total=client.file.len();
让mut done=0;
对于client.file.iter()中的j{
如果*j==1{
完成+=1;
}
}
让百分比=(按f64完成/按f64总计)*100.0;
让client_per:String=format!(“client{}:{.2}%\n”,client.id,percentage);
MutexGuard::解锁公平(客户端);
显示+=&每台客户机;
}
MutexGuard::解锁公平(客户端arr_c);
println!(“\x1B[2J\x1B[1;1H{}”,显示);
});
让client_arr_c=Arc::clone(&client_arr);
std::thread::spawn(移动| |){
对于0..max\u客户端中的i{
线程::睡眠(持续时间::从_毫秒(1000));
让mut file_init=0;
如果i==0{
文件_init=1;
}
让client=Arc::new(互斥体::new(客户端{
id:i+1,
文件:vec![file\u init;file\u size],
}));
让clientc=Arc::clone(&client);
让req_qc=Arc::clone(&req_q);
线程::生成(移动| |{
对于0.文件大小中的j{
线程::睡眠(持续时间::从_毫秒(10));
让clientcc=clientc.lock();
如果clientcc.file[j]==0{
let req=就绪信号{
客户端:Arc::clone(&clientc),
第j节,
};
要求质量控制推送(要求);
MutexGuard::unlock_fair(clientcc);
继续;
}
}
});
让clientc=Arc::clone(&client);
让req_qc=Arc::clone(&req_q);
线程::生成(移动| |循环{
线程::睡眠(持续时间::从_毫秒(20));
设clientc=clientc.lock();
如果请求质量控制为空(){
继续;
}
让req=req_qc.pop().unwrap();
如果clientc.file[req.section]==1{
让mut req_client=req.client.lock();
req_客户机文件[req.section]=1;
MutexGuard::解锁公平(请求客户端);
MutexGuard::unlock_fair(clientc);
}否则{
要求质量控制推送(要求);
}
});
让clientc=Arc::clone(&client);
client_arr_c.lock().push(clientc);
}
});
循环{}
}
依赖项是
crossbeam=“0.7.3”
parking\u lot=“0.11.0”
。 最好在一个新的货运项目中运行它,而不是在操场上运行,因为那里的标准格式混乱不堪

这个程序的基本思想是模拟一个简单的p2p网络。第一个客户端已经有了文件(
client.file
有一个
Vec
填充了1s)。一个新的客户端每秒加入一个没有文件(
client.file
有一个
Vec
填充了0s)。创建新客户端后,立即为该客户端创建2个线程。第一个线程(第62-77行)检查客户端文件的哪些部分为0,并将每个部分的请求放入队列中。第二个线程(第80-97行)从队列中取出请求,并将这些0更改为1。 开始处的线程(第24-47行)用于检查进度并通过在包含所有客户端的vec上循环打印输出,每10毫秒一次


如果您运行此程序,您可以看到每个客户端的百分比不断增加,直至停止。这一点因运行而异。所有客户端的预期行为都会达到100%。但这种预期行为只发生过很少一次。是什么导致了死锁?我如何停止死锁?

根据我的经验,死锁是由什么引起的死锁的主要来源是同时锁定两个原语。例如:

线程1:
锁定
锁B
...
解锁B
解锁
线程2:
锁B
锁定
...
解锁
解锁B
如果两个线程或多或少同时启动,线程1可能会锁定A,线程2可能会锁定B,然后…死锁

如果无法避免锁定两个对象,显而易见的解决方法是始终以相同的顺序锁定它们:先锁定A,然后锁定B。解锁顺序并不重要

在代码中,有两个位置同时锁定了多个对象:

  • 主线程总是先锁定阵列,然后再按顺序锁定内部的每个客户端。这不会导致死锁,因为阵列锁定的唯一其他位置是最后一行
    client\u arr\u c.lock().push(clientc)
    ,并且没有其他锁
  • 对请求进行出列的线程会锁定当前客户端和请求者客户端。问题出在这里
  • 重写该代码揭示了另一个问题:
    is_empty()
    pop().unwrap()
    之间存在竞争。在这两者之间,另一个线程可能会进入并清空队列。执行
    pop().unwrap()
    时,您会死机(请注意,客户端锁不会保护队列)

    弹出队列的竞争很容易解决:<
    use crossbeam::queue::SegQueue;
    use parking_lot::{Mutex, MutexGuard};
    use std::sync::Arc;
    use std::thread;
    use std::time::Duration;
    
    struct ReadySignal {
        client: Arc<Mutex<Client>>,
        section: usize,
    }
    
    struct Client {
        id: usize,
        file: Vec<usize>,
    }
    
    fn main() {
        let max_clients = 10;
        let file_size = 1000;
        let req_q = Arc::new(SegQueue::new());
        let v: Vec<Arc<Mutex<Client>>> = Vec::new();
        let client_arr = Arc::new(Mutex::new(v));
        let client_arr_c = Arc::clone(&client_arr);
        thread::spawn(move || loop {
            thread::sleep(Duration::from_millis(10));
            let mut display = String::new();
            let client_arr_c = client_arr_c.lock();
            for i in client_arr_c.iter() {
                let client = i.lock();
                let total = client.file.len();
                let mut done = 0;
                for j in client.file.iter() {
                    if *j == 1 {
                        done += 1;
                    }
                }
                let percentage = (done as f64 / total as f64) * 100.0;
                let client_per: String = format!("Client {}: {:.2}%\n", client.id, percentage);
                MutexGuard::unlock_fair(client);
                display += &client_per;
            }
            MutexGuard::unlock_fair(client_arr_c);
            println!("\x1B[2J\x1B[1;1H{}", display);
        });
        let client_arr_c = Arc::clone(&client_arr);
        std::thread::spawn(move || {
            for i in 0..max_clients {
                thread::sleep(Duration::from_millis(1000));
                let mut file_init = 0;
                if i == 0 {
                    file_init = 1;
                }
                let client = Arc::new(Mutex::new(Client {
                    id: i + 1,
                    file: vec![file_init; file_size],
                }));
                let clientc = Arc::clone(&client);
                let req_qc = Arc::clone(&req_q);
                thread::spawn(move || {
                    for j in 0..file_size {
                        thread::sleep(Duration::from_millis(10));
                        let clientcc = clientc.lock();
                        if clientcc.file[j] == 0 {
                            let req = ReadySignal {
                                client: Arc::clone(&clientc),
                                section: j,
                            };
                            req_qc.push(req);
                            MutexGuard::unlock_fair(clientcc);
                            continue;
                        }
                    }
                });
                let clientc = Arc::clone(&client);
                let req_qc = Arc::clone(&req_q);
                thread::spawn(move || loop {
                    thread::sleep(Duration::from_millis(20));
                    let clientc = clientc.lock();
                    if req_qc.is_empty() {
                        continue;
                    }
                    let req = req_qc.pop().unwrap();
                    if clientc.file[req.section] == 1 {
                        let mut req_client = req.client.lock();
                        req_client.file[req.section] = 1;
                        MutexGuard::unlock_fair(req_client);
                        MutexGuard::unlock_fair(clientc);
                    } else {
                        req_qc.push(req);
                    }
                });
                let clientc = Arc::clone(&client);
                client_arr_c.lock().push(clientc);
            }
        });
        loop {}
    }
    
    let req = match req_qc.pop() {
        Err(_) => continue,
        Ok(x) => x,
    };
    
    struct ReadySignal {
        client_id: usize, //same as client.id, but unlocked
        client:  Arc<Mutex<Client>>,
        section: usize,
    }
    
    let (clientc, mut req_client) = match client_id.cmp(&req.client_id) {
        std::cmp::Ordering::Less => {
            let a = clientc.lock();
            let b =  req.client.lock();
            (a, b)
        }
        std::cmp::Ordering::Greater => {
            let b =  req.client.lock();
            let a = clientc.lock();
            (a, b)
        }
        std::cmp::Ordering::Equal => {
            req_qc.push(req);
            continue;
        }
    };
    
    use std::thread;
    use std::time::{Duration};
    use std::sync::Arc;
    use crossbeam::queue::SegQueue;
    use parking_lot::{Mutex, MutexGuard};
    
    struct ReadySignal {
        client_id: usize,
        client:  Arc<Mutex<Client>>,
        section: usize,
    }
    
    struct Client {
        id:      usize,
        file:    Vec<usize>,
    }
    
    fn main() {
        let max_clients = 10;
        let file_size = 1000;
        let req_q = Arc::new(SegQueue::new());
        let v: Vec<Arc<Mutex<Client>>> = Vec::new();
        let client_arr = Arc::new(Mutex::new(v));
        let client_arr_c = Arc::clone(&client_arr);
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_millis(10));
                let mut display = String::new();
                let client_arr_c = client_arr_c.lock();
                for (_ii, i) in client_arr_c.iter().enumerate() {
    
                    let client = i.lock();
                    let total = client.file.len();
                    let mut done = 0;
                    for j in client.file.iter() {
                        if *j == 1 {
                            done += 1;
                        }
                    }
                    let percentage = (done as f64/total as f64) * 100.0;
                    let client_per: String = format!("Client {}: {:.2}%\n", client.id, percentage);
                    MutexGuard::unlock_fair(client);
                    display += &client_per;
                }
                MutexGuard::unlock_fair(client_arr_c);
                println!("\x1B[2J\x1B[1;1H{}",display);
                
            }
        });
        let client_arr_c = Arc::clone(&client_arr);
        std::thread::spawn(move || {
            for i in 0..max_clients {
                thread::sleep(Duration::from_millis(1000));
                let mut file_init = 0;
                if i == 0 {
                    file_init = 1;
                }
                let client_id = i + 1;
                let client = Arc::new(Mutex::new(Client{
                    id: client_id,
                    file: vec![file_init;file_size],
                }));
                let clientc = Arc::clone(&client);
                let req_qc = Arc::clone(&req_q);
                thread::spawn(move || {
                    for j in 0..file_size {
                        thread::sleep(Duration::from_millis(10));
                        let clientcc = clientc.lock();
                        if clientcc.file[j] == 0 {
                            let req = ReadySignal{
                                client_id,
                                client: Arc::clone(&clientc),
                                section: j,
                            };
                            MutexGuard::unlock_fair(clientcc);
                            req_qc.push(req);
                            continue;
                        }
                        
                    }
                });
                let clientc = Arc::clone(&client);
                let req_qc = Arc::clone(&req_q);
                thread::spawn(move || {
                    loop {
                        thread::sleep(Duration::from_millis(20));
                        let req = match req_qc.pop() {
                            Err(_) => continue,
                            Ok(x) => x,
                        };
                        let (clientc, mut req_client) = match client_id.cmp(&req.client_id) {
                            std::cmp::Ordering::Less => {
                                let a = clientc.lock();
                                let b =  req.client.lock();
                                (a, b)
                            }
                            std::cmp::Ordering::Greater => {
                                let b =  req.client.lock();
                                let a = clientc.lock();
                                (a, b)
                            }
                            std::cmp::Ordering::Equal => {
                                req_qc.push(req);
                                continue;
                            }
                        };
                        if clientc.file[req.section] == 1 {
                            req_client.file[req.section] = 1;
                            MutexGuard::unlock_fair(req_client);
                            MutexGuard::unlock_fair(clientc);
                        } else {
                            MutexGuard::unlock_fair(req_client);
                            MutexGuard::unlock_fair(clientc);
                            req_qc.push(req);
                        }
                    }
                });
                let clientc = Arc::clone(&client);
                client_arr_c.lock().push(clientc);
            }
        });
        loop {
            thread::sleep(Duration::from_millis(1000));
        }
    }