等待控制台读取输入

9
我正在构建一个异步控制台应用程序,在其中我创建了处理应用程序不同区域的类。
我创建了一个InputHandler类,希望它能等待Console.ReadLine()输入。然而,由于它不是异步函数,无法等待该函数。目前的解决方案是简单地:
private async Task<string> GetInputAsync() {
    return Task.Run(() => Console.ReadLine())
}

代码运行得非常好。然而,我的(有限的)理解是调用Task.Run会触发一个新的(并行?)线程。这会破坏异步方法的目的,因为现在该新线程被阻塞直到Readline()返回了对吗?

我知道线程是昂贵的资源,所以我感觉这样做既浪费又不专业。我也尝试过使用Console.In.ReadLineAsync()但它显然有缺陷吗?(它似乎会挂起)。


1
嗯,不,这并没有违背异步方法的目的。它的目的是不阻塞调用者。这当然需要一个线程,而Console.ReadLine()则是一个阻塞调用。 - Hans Passant
1
线程并不一定是昂贵的资源,只要你不过度使用它们,或者在某些情况下需要它们。如果你的控制台应用程序在等待Console.ReadLine()时在后台处理一些东西,那么使用线程是个好主意。 - Ryan
2
这样写不是应该是 return await Task.Run( ( ) => Console.ReadLine( ) ); 吗?我按照你的方式尝试了一下,但是编译器报错了... - Tarc
1个回答

18
我知道线程是昂贵的资源,所以我觉得这样做非常浪费和hacky。我也尝试过Console.In.ReadLineAsync(),但它似乎有bug?(它似乎会挂起)。
不幸的是,控制台流确实具有令人惊讶的行为。其根本原因是它们为了保证控制台流的线程安全而阻塞。个人认为,在异步方法中阻塞是一个糟糕的设计选择,但Microsoft决定这样做(仅适用于控制台流),并且坚持他们的决定
因此,如果您确实想要异步读取,这个API设计强制您使用后台线程(例如,Task.Run)。这不是您通常应该使用的模式,但在这种情况下(控制台流),这是一种可接受的hack来解决他们的API问题。

然而,我的(有限的)理解是调用Task.Run将会启动一个新的(并行的?)线程。

并不完全正确。 Task.Run 会将一些工作排队到线程池中,其中一个线程将执行代码。线程池根据需要管理线程的创建,通常无需担心它。因此,Task.Run 不像每次都创建新线程那样浪费资源。


那个微软的链接已经失效了。我本来想自己修复它,但是我找不到替代品。 - MarredCheese
2
是的,他们一段时间前删除了所有的错误报告。我猜这是达到零缺陷的一种方式... - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接