如何可靠地清理执行阻塞IO的Rust线程?

sle*_*row 10 multithreading channel blocking rust

在Rust中生成阻塞IO的线程似乎是一个常见的习惯用法,因此您可以使用非阻塞通道:

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;
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

问题是,重新加入这样的线程取决于生成的线程"意识到"通道的接收端已经被丢弃(即,调用send(..)返回Err(_)):

drop(accept_rx);
listener_thread.join(); // blocks until listener thread reaches accept_tx.send(..)
Run Code Online (Sandbox Code Playgroud)

你可以为TcpListeners 创建虚拟连接,并TcpStream通过克隆关闭s,但这些似乎是清理这些线程的真正hacky方法,而且就目前而言,我甚至不知道在读取时触发线程阻塞的黑客攻击stdin加入.

我怎样才能清理这些线程,或者我的架构是错的?

Spa*_*apS 3

在 Windows 或 Linux/Unix/POSIX 中,人们根本无法安全可靠地取消线程,因此 Rust 标准库中不提供该线程。

这是关于它的内部讨论

强制取消线程会带来很多未知数。它可能会变得非常混乱。除此之外,线程和阻塞 I/O 的组合将始终面临这个问题:您需要每个阻塞 I/O 调用都有超时,以便有机会可靠地中断。如果无法编写异步代码,则需要使用进程(具有定义的边界并且可以由操作系统强制结束,但显然会带来更重的重量和数据共享挑战)或非阻塞 I/O,这将将您的线程重新置于可中断的事件循环中。

mio可用于异步代码。Tokio是一个基于 mio 的更高级别的包,它使得编写非阻塞异步代码更加直接。