我的服务器在可以安全尝试连接时使用Barrier
通知客户端。没有障碍,我们将冒随机失败的风险,因为无法保证服务器套接字将被绑定。
现在,假设服务器出现紧急情况-例如,尝试将套接字绑定到端口80。客户端将永远处于wait()
状态。我们无法join()
来确定服务器线程是否崩溃,因为join()
是阻塞操作-如果我们join()
无法connect()
。
假设std::sync
API不向方法提供超时,那么执行这种同步的正确方法是什么?
这只是一个MCVE来演示此问题。我在单元测试中有一个类似的案例-它永远运行着。
use std::{
io::prelude::*,
net::{SocketAddr, TcpListener, TcpStream},
sync::{Arc, Barrier},
};
fn main() {
let port = 9090;
//let port = 80;
let barrier = Arc::new(Barrier::new(2));
let server_barrier = barrier.clone();
let client_sync = move || {
barrier.wait();
};
let server_sync = Box::new(move || {
server_barrier.wait();
});
server(server_sync, port);
//server(Box::new(|| { no_sync() }), port); //use to test without synchronisation
client(&client_sync, port);
//client(&no_sync, port); //use to test without synchronisation
}
fn no_sync() {
// do nothing in order to demonstrate the need for synchronization
}
fn server(sync: Box<Fn() + Send + Sync>, port: u16) {
std::thread::spawn(move || {
std::thread::sleep_ms(100); //there is no guarantee when the os will schedule the thread. make it 100% reproducible
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let socket = TcpListener::bind(&addr).unwrap();
println!("server socket bound");
sync();
let (mut client, _) = socket.accept().unwrap();
client.write_all(b"hello mcve").unwrap();
});
}
fn client(sync: &Fn(), port: u16) {
sync();
let addr = SocketAddr::from(([127, 0, 0, 1], port));
let mut socket = TcpStream::connect(&addr).unwrap();
println!("client socket connected");
let mut buf = String::new();
socket.read_to_string(&mut buf).unwrap();
println!("client received: {}", buf);
}
答案 0 :(得分:2)
在这里我将使用Condvar
代替Barrier
。
要真正解决您的问题,我至少看到了三种可能的解决方案:
Condvar::wait_timeout
并将超时设置为合理的持续时间(例如1秒,足以绑定到端口)Mutex
是否中毒。Mutex
来代替Condvar
(确保Mutex
首先被另一个线程锁定),然后使用Mutex::try_lock
进行检查如果Mutex
is poisoned 我认为与第三个解决方案相比,应该更喜欢解决方案1或2,因为您将避免确保另一个线程首先锁定了Mutex
。