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

Posted

技术标签:

【中文标题】如何可靠地清理执行阻塞 IO 的 Rust 线​​程?【英文标题】:How can I reliably clean up Rust threads performing blocking IO? 【发布时间】:2015-09-03 13:42:06 【问题描述】:

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

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;
            
        
    );

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

drop(accept_rx);
listener_thread.join(); // blocks until listener thread reaches accept_tx.send(..)

您可以为TcpListeners 建立虚拟连接,并通过克隆关闭TcpStreams,但这些似乎是清理此类线程的非常老套的方法,就目前而言,我什至不知道hack 触发线程阻塞,从stdin 读取加入。

我该如何清理这样的线程,还是我的架构有问题?

【问题讨论】:

对于无法立即可靠清理的线程,一般的模式是通知它们清理,让它们运行并确保它们不再产生副作用。对于不可接受的 TcpListener,但对于传出请求或文件操作,它通常是可接受的。 @usr 是的,但我正在寻找 Rust 解决方案。 (我猜它不需要完全适合那个模具。)我问这个问题是因为我无法找到使用当前(安全)API 的方法。 rust-lang.org 【参考方案1】:

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

Here is an internals discussion about it.

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

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

【讨论】:

以上是关于如何可靠地清理执行阻塞 IO 的 Rust 线​​程?的主要内容,如果未能解决你的问题,请参考以下文章

如何确定 Rust 中进程的有效用户 ID?

Rust futures: async fn 中的 thread::sleep 和阻塞调用

# Rust异步网络编程

使用期货的 Rust 回显服务器和客户端永远阻塞自己

终止python程序的正确方法,无论调用位置如何,并进行清理

socket.io 下的可靠消息传递?