我的服务器使用Barrier
通知客户端何时可以安全地尝试连接。没有障碍,我们冒着随机失败的风险,因为无法保证服务器套接字会被绑定。
现在假设服务器发生恐慌 - 例如尝试将套接字绑定到端口80.客户端将永远留在wait()
-ing。我们不能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);
}
而不是Barrier
我会在这里使用Condvar
。
为了真正解决您的问题,我发现至少有三种可能的解决方案:
Condvar::wait_timeout
并将超时设置为合理的持续时间(例如,1秒应足以绑定到端口)Mutex
是否中毒。Condvar
,你可以使用普通的Mutex
(确保Mutex
首先被另一个线程锁定)然后使用Mutex::try_lock
来检查Mutex
is poisoned我认为应该优先考虑解决方案1或2而不是第三个,因为你将避免确保另一个线程首先锁定了Mutex
。