multithreading - 为什么 Rust 互斥锁似乎没有将锁给最后想要锁定它的线程?
问题描述
我想编写一个程序,它产生两个线程来锁定 a Mutex
,增加它,打印一些东西,然后解锁,Mutex
以便另一个线程可以做同样的事情。我添加了一些睡眠时间以使其更加一致,所以我认为输出应该是这样的:
ping pong ping pong …
但实际输出是相当随机的。大多数时候,它只是
ping ping ping … pong
但是根本没有一致性。有时中间也有一个“乒乓”。
我相信互斥体有某种方法可以确定谁想最后锁定它,但看起来情况并非如此。
- 锁定实际上是如何工作的?
- 如何获得所需的输出?
use std::sync::{Arc, Mutex};
use std::{thread, time};
fn main() {
let data1 = Arc::new(Mutex::new(1));
let data2 = data1.clone();
let ten_millis = time::Duration::from_millis(10);
let a = thread::spawn(move || loop {
let mut data = data1.lock().unwrap();
thread::sleep(ten_millis);
println!("ping ");
*data += 1;
if *data > 10 {
break;
}
});
let b = thread::spawn(move || loop {
let mut data = data2.lock().unwrap();
thread::sleep(ten_millis);
println!("pong ");
*data += 1;
if *data > 10 {
break;
}
});
a.join().unwrap();
b.join().unwrap();
}
解决方案
Mutex
并且RwLock
两者都遵循特定于操作系统的原语,并且不能保证是公平的。在 Windows 上,它们都使用SRW 锁实现,这些锁被明确记录为不公平。我没有对其他操作系统进行研究,但你绝对不能依赖公平性std::sync::Mutex
,特别是如果你需要这个代码是可移植的。
Rust 中一个可能的解决方案是 crateMutex
提供的实现,它提供了一个方法,该方法是用公平算法实现的。parking_lot
unlock_fair
默认情况下,互斥锁是不公平的,它允许当前线程在另一个线程有机会获得锁之前重新锁定互斥锁,即使该线程已经在互斥锁上阻塞了很长时间。这是默认设置,因为它避免了在每个互斥锁解锁时强制进行上下文切换,从而允许更高的吞吐量。这可能导致一个线程比其他线程获取互斥锁的次数更多。
但是,在某些情况下,如果有一个等待线程,则强制将锁传递给等待线程,从而确保公平性可能是有益的。这是通过使用此方法而不是
MutexGuard
正常删除来完成的。
虽然parking_lot::Mutex
没有专门使用该unlock_fair
方法并不声称是公平的,但我发现您的代码仅通过进行该开关(操场)产生与乒乓球相同数量的 ping,甚至不使用该unlock_fair
方法。
通常,当守卫超出范围时,互斥锁会自动解锁。为了让它公平地解锁,你需要在防护被丢弃之前插入这个方法调用:
let b = thread::spawn(move || loop {
let mut data = data1.lock();
thread::sleep(ten_millis);
println!("pong ");
*data += 1;
if *data > 10 {
break;
}
MutexGuard::unlock_fair(data);
});
推荐阅读
- python - 如何在 python 中使用 svgwrite 以编程方式调整 SVG 文件的大小?
- core-web-vitals - 如何在启用 Google Adsense“让 Google 优化您的移动广告尺寸”的情况下避免 CLS(累积布局转换)?
- javascript - 在聊天应用程序(Firestore)中收听实时更新的最佳方式?
- r - 来自汇总统计的组指定的 geom_boxplot 无法在 r 中生成箱线图
- rabbitmq - 用nestjs改变rabbitmq交换
- flutter - Flutter:如何访问自定义小部件中的密钥
- tensorflow - 为什么相同的 TensorFlow 模型适用于数组列表,但不适用于未批处理的 tf.data.Dataset?
- real-time - 无法在 systemd 服务上设置实时优先级
- java - 查询 dsl - 如何编写有子句
- kubernetes - 如何使用 Helm Spray 重新部署 pod?