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