Windows 控制台应用程序中的多线程 UI






4.18/5 (11投票s)
在Windows控制台应用程序中使用多线程,而不中断用户输入。
引言
MultithreadedConsole是一个解决方案,可以帮助程序员轻松地在Windows控制台应用程序中实现多线程UI。
背景
最近,我正在进行一个需要非常简单的用户输入的 多线程实现。我决定一个Windows控制台应用程序就足够了。
随着时间的推移,项目也越来越大,我希望向用户展示一些关于应用程序内部情况的反馈。我创建了一些事件,并使用Console.WriteLine
显示数据。
这在用户决定在控制台中输入命令之前都运行良好。正如您可能已经猜到的,当多线程运行时,这并不奏效。用户输入会与第二线程打印的文本混合在一起,然后会开始一个新行的空行。
经过更多的谷歌搜索,我没有找到任何易于使用的解决方案来解决这个问题,于是我决定自己写一个。我想与社区分享我的解决方案。
对我来说,最好的解决方案似乎是将来自另一个线程的文本添加到用户行之前。这需要存储用户输入,然后在另一个线程写入时覆盖该输入,并在下一行恢复用户输入。
这听起来可能足够简单,但根据我编写这篇文章的经验,我知道这并不简单。问题在于拦截用户输入。虽然使用Console.ReadKey
可以做到这一点,但它也禁用了像使用箭头键滚动浏览先前命令这样的功能。前面提到的文章解决了这个问题,所以我想将它重用于这个解决方案。
使用代码
该库(已附上)提供了自己的ConsoleExt.WriteLine
方法,该方法与.NET提供的Console.WriteLine
方法非常相似。不同之处在于,新方法实际上会拦截用户的所有输入,将其存储起来,然后写入该行。这提供了使用输入信息来,在这种情况下,将其写在初始输入被覆盖之后的新行上的可能性。
信息线程应该使用的方法名为ConsoleExt.PrependLine
。该方法会在控制台的用户输入行之前添加一行。
让我们来看一个例子。
private static bool _running = true;
static void Main(string[] args)
{
Thread eventThread = new Thread(ThreadMethod);
eventThread.Start();
while (_running)
{
var line = ConsoleExt.ReadLine();
// ..
}
}
static void ThreadMethod()
{
while (_running)
{
for (int i = 0; i < 40; i++)
{
if (!_running)
return;
Thread.Sleep(100);
}
ConsoleExt.PrependLine("This is an event on a different thread!");
}
}
这个例子模拟了每4秒从另一个线程触发一个事件。不是使用WriteLine
方法,而是使用PrependLine
。结果将如下面所示。
就这样。一个易于使用的解决方案,可以节省大量的挫败感。无论是对程序员还是用户。
关注点
该解决方案是线程安全的。多个线程可以调用ConsoleExt.PrependLine
。
当我寻找解决这个问题的方案时,我确实发现其他人也遇到了同样的问题。这主要是实现控制台聊天应用程序的案例。这个解决方案也可以用于此类情况。
当然,也很容易使用Console.CursorLeft
和Console.CursorTop
来覆盖来自另一个线程的加载项。但是,本文的目的是演示一种易于使用的、可重用的多线程控制台应用程序用法。(可以考虑聊天示例)。
我还没有实现所有默认的控制台功能。如果您有任何好的补充,欢迎给我留言,我可能会将其添加到项目中。
ConsoleExt
支持更多有用的控制台方法。这篇文章提供了更多关于这些的信息。
给纯粹主义者的小免责声明:尽管我一直致力于编写干净的代码,但 ConsoleExt
类中包含一个巨大的 switch
语句。经过许多不眠之夜,我故意这样保留了它。在我看来,将其分解成更小的部分实际上会降低代码的可读性。
历史
2017年5月3日 - 版本 1
2017年8月19日 - 版本 1.1
- 重构了
ConsoleExt
以允许更多的单元测试,并实现了那些单元测试。