在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;
}
}
});
}
问题是,重新加入这样的线程取决于生成的线程"实现"已删除频道的接收端(即,呼叫send(..)
返回Err(_)
):
drop(accept_rx);
listener_thread.join(); // blocks until listener thread reaches accept_tx.send(..)
您可以为TcpListener
创建虚拟连接,并通过克隆关闭TcpStream
s,但这些似乎是非常简单的方法来清理这些线程,而且就目前而言,我不会&#39甚至知道一个 hack 来触发一个线程阻塞来自stdin
的读取加入。
如何清理这些线程,或者我的架构错了?
答案 0 :(得分:2)
在Windows或Linux / Unix / POSIX中无法安全地取消线程,因此在Rust标准库中无法使用它。
Here is an internals discussion about it
强行取消线程有很多未知因素。它可能变得非常混乱。除此之外,线程和阻塞I / O的组合将始终面临这个问题:您需要每个阻塞I / O调用都有超时,甚至有可能可靠地中断。如果不能编写异步代码,则需要使用进程(具有已定义的边界并且可以强制结束操作系统,但显然会带来更重的权重和数据共享挑战)或非阻塞I / O这将使你的线程回到可中断的事件循环中。
答案 1 :(得分:-1)
tldr;虚拟连接可能是最简单的方法。
(我假设linux为os。)
listener.incoming()将在其.next()方法中调用TcpListener上的.accept() 并且线程将被卡在对os的accept调用中。据我所知,只能通过连接尝试或信号或者套接字被设置为非阻塞而自愿带回。
生锈标准库似乎不支持信号处理。
套接字的文件描述符似乎在TcpListener中不可访问,因此您无法将其设置为非阻塞模式。还有那个 这意味着投票,这可能是一个坏主意。
另一种方法可能是使用mio,因为它提供了一个事件循环。您可以根据事件循环定制整个应用程序,但不需要 做线程,或者你可以为每个线程使用一个可能阻塞的事件循环 并让它听一个额外的管道,所以你可以把它唤醒,它可以 闭嘴自己。第一个可能不再可行,取决于如何 你已经拥有许多代码而第二个代码听起来有点过分。