65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.18/5 (11投票s)

2017年5月4日

CPOL

3分钟阅读

viewsIcon

32270

downloadIcon

486

在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以允许更多的单元测试,并实现了那些单元测试。
© . All rights reserved.