multithreading - ¿Cómo puedo limpiar de manera confiable los subprocesos de Rust que realizan bloqueos de IO?
blocking channel (2)
Parece ser una expresión común en Rust generar un hilo para bloquear IO para que puedas usar canales que no sean de bloqueo:
use std::sync::mpsc::channel;
use std::thread;
use std::net::TcpListener;
fn main() {
let (accept_tx, accept_rx) = channel();
let listener_thread = thread::spawn(move || {
let listener = TcpListener::bind(":::0").unwrap();
for client in listener.incoming() {
if let Err(_) = accept_tx.send(client.unwrap()) {
break;
}
}
});
}
El problema es que volver a unir hilos como este depende de que el hilo generado "se dé cuenta" de que el extremo receptor del canal ha sido descartado (es decir, al llamar send(..)
devuelve Err(_)
):
drop(accept_rx);
listener_thread.join(); // blocks until listener thread reaches accept_tx.send(..)
Puede hacer conexiones ficticias para TcpListener
s, y apagar TcpStream
s a través de un clon, pero estas parecen ser realmente extravagantes maneras de limpiar tales subprocesos, y tal como están las cosas, ni siquiera sé de un truco para activar un bloqueo de subprocesos en una lectura de stdin
para unirse.
¿Cómo puedo limpiar hilos como estos o mi arquitectura está equivocada?
Uno simplemente no puede cancelar un hilo de forma segura en Windows o Linux / Unix / POSIX, por lo que no está disponible en la biblioteca estándar de Rust.
Aquí hay una discusión interna al respecto .
Hay muchas incógnitas que provienen de cancelar hilos a la fuerza. Puede ser realmente complicado. Más allá de eso, la combinación de subprocesos y E / S de bloqueo siempre enfrentará este problema: necesita que cada llamada de E / S de bloqueo tenga tiempos de espera para que tenga la posibilidad de ser interrumpible de manera confiable. Si no se puede escribir un código asíncrono, uno necesita usar procesos (que tienen un límite definido y el SO puede terminarlo a la fuerza, pero obviamente con mayores desafíos de peso y de intercambio de datos) o E / S sin bloqueo que aterriza tu hilo en un bucle de evento que es interrumpible.
mio está disponible para el código asíncrono. Tokio es una caja de nivel superior basada en mio que hace que escribir código asíncrono sin bloqueo sea aún más directo.
tldr; La conexión ficticia podría ser la forma más fácil.
(Supongo que Linux es el sistema operativo).
listener.incoming () llamará a .accept () en el TcpListener en su método .next () y el hilo quedará bloqueado en la llamada de aceptación al sistema operativo. A mi leal saber y entender, solo puede ser devuelto voluntariamente por un intento de conexión o una señal o si el socket está configurado como no bloqueante.
el manejo de la señal no parece ser compatible con las bibliotecas estándar de herrumbre.
El descriptor de archivo del socket no parece estar accesible en el TcpListener, por lo tanto, no puede establecerlo en modo no bloqueante. También eso implicaría encuestas, eso podría ser una mala idea.
Una alternativa podría ser usar mio ya que proporciona un bucle de evento. Podrías adaptar toda tu aplicación al ciclo de eventos y no necesitas hacer subprocesos, o podrías usar un ciclo de eventos para cada hilo, que podría bloquear y dejar que escuche un tubo adicional, para que puedas reactivarlo. que puede cerrarse solo El primero podría no ser factible, dependiendo de la cantidad de código que ya tenga, y el segundo parece excesivo.