我目前正在构建一个异步控制台应用程序,其中我创建了类来处理应用程序的不同区域。
我创建了一个 InputHandler 类,我设想它会等待 Console.ReadLine() 输入。但是,您不能等待这样的函数(因为它不是异步的),我当前的解决方案就是简单地:
private async Task<string> GetInputAsync() {
return Task.Run(() => Console.ReadLine())
}
运行完美。然而,我(有限)的理解是调用 Task.Run 将触发一个新的(并行?)线程。这违背了异步方法的目的,因为新线程现在被阻塞,直到 Readline() 返回,对吗?
我知道线程是一种昂贵的资源,所以我觉得这样做真的很浪费而且很笨拙。我也尝试过 Console.In.ReadLineAsync() 但它显然有问题? (好像挂了)。
不幸的是,控制台流确实有令人惊讶的行为。根本原因是它们“阻止”以确保控制台流的线程安全。就我个人而言,我认为阻塞异步方法是一个糟糕的设计选择,但微软决定这样做(仅适用于控制台流)并且我知道线程是一种昂贵的资源,所以我觉得这样做真的很浪费而且很笨拙。我也尝试过 Console.In.ReadLineAsync() 但它显然有问题? (好像挂了)。
坚持他们的决定。 因此,如果您确实想真正异步读取,则此 API 设计强制
您使用后台线程(例如,Task.Run
)。这不是您通常应该使用的模式,但在这种情况下(控制台流),这是一种可以接受的破解 API 的方法。
但是,我(有限)的理解是调用 Task.Run 将触发一个新的(并行?)线程。
会将一些工作排队到线程池中,线程池将让其线程之一执行代码。线程池根据需要管理线程的创建,您通常不必担心它。因此,不完全是。
Task.Run
Task.Run
并不像每次实际创建一个新线程那么浪费。虽然我同意
Console.ReadLine()
和Console.In
都使用同步流,正如Stephen提到的(第一个实际上只是包装了第二个)。但是,底层原始
Stream
(可通过
OpenStandardInput()
访问)是not
同步的,它被包装在
StreamReader
级别的同步助手中。因此,一种不产生额外线程的可能解决方案是创建我们自己的 StreamReader
,而不是使用
Console
提供的:
using var inputReader = new StreamReader(Console.OpenStandardInput(), Console.InputEncoding);
while (await sr.ReadLineAsync() is string line)
{
...
}