4

最近,我开始使用 VB.Net 开发基于多线程控制台的应用程序,并且遇到(可解释的)错误。该程序本身是一个服务器,它从不同线程中的外部应用程序接收套接字连接。我还有一个单独的线程,它只是等待用户使用 console.readline 在应用程序中输入文本(以便可以输入服务器命令,例如 HELP 或 STATUS)。

问题是程序从不同的套接字线程输出消息,如果我正在输入命令,它就会被破坏。如果我输入命令“状态”,它应该可以正常工作,但是对于输入命令的用户,它会被分解,他们可能无法查看他们是否输入了错误的命令。例如:

staNew 客户端已连接。

我知道为什么会发生这种情况,但我很好奇是否有一个简单的修复方法可以停止这种情况(无需在输入期间暂停其他线程),或者可能有一种更简单的方法来允许输入不会移动的控制台命令额外的应用程序输出。

4

5 回答 5

1

几点建议:

  • 将消息写入队列而不是直接写入控制台。当用户输入命令时,从队列中拉出所有最近的消息并将它们转储到控制台。
  • 将消息输出到控制台的不同区域,例如旧的 BBS 式聊天 UI。将页面的一部分用于消息,另一部分用于输入和用户命令的结果。这需要在控制台端做更多的工作。
  • 将消息输出到一个单独的日志文件,用户可以使用tail或类似的东西,一个或应用程序,用户可以同时打开两个应用程序,一个显示消息,另一个以交互方式运行命令并查看输出。
于 2012-09-06T12:36:49.137 回答
1

C Sharp 等效

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;

namespace testconsole
{
    class Program
    {
        private static bool Running = true;
        private static string Command = "";

        static void Main( string[] args )
        {
            while ( Running )
            {
                ReadInput();
            }//running
        }//main-----------------------------------------

        static void ReadInput()
        {
            ConsoleKeyInfo cki;

            cki = Console.ReadKey();

            if ( cki.Key == ConsoleKey.Escape )
            {
                Command = "";
            }
            else if (cki.Key == ConsoleKey.Backspace)
            {
                ClearConsole();
                if ( Command.Length > 0 )
                {
                    Command = Command.Substring( 0, Command.Length - 1 );
                }
                Console.CursorLeft = 0;
                Console.Write( Command );
            }
            else if ( cki.Key == ConsoleKey.Enter )
            {
                Console.CursorLeft = 0;
                ClearConsole();
                string TempCommand = Command;
                TextOut( Command );
                Command = "";
                //process tempcommand
                if ( TempCommand  == "quit")
                {
                    Running = false;
                }
                if ( TempCommand == "test" )
                {
                    TextOut("this is a test");
                }
            }
            else
            {
                Command += cki.KeyChar;
                Console.CursorLeft = 0;
                Console.Write( Command );
            }
        }//ReadInput-----------------------------------------

        static void ClearConsole()
        {
            for ( int i = 0; i < Command.Length; i++ )
            {
                Console.Write( " " );
            }
        }//ClearConsole-----------------------------------------

        static void TextOut( string Message )
        {
            if (Command != "")
            {
                ClearConsole();
            }
            Console.CursorLeft = 0;
            Console.WriteLine( Message );
            if ( Message != Command )
            {
                Console.Write( Command );
            }
        }//TextOut-----------------------------------------

    }//program
}//namespace
于 2013-04-14T21:39:55.870 回答
1

这是一个稍微不同的版本,它支持 readlines 上的前缀并打包到它自己的类中以供重用。它还使用 StringBuilder 而不是 readline 的字符串。

public static class Console2
{
    private static bool _isReading;
    private static string _currentPrefix;
    private static readonly StringBuilder CurrentReadLine = new StringBuilder();

    public static string ReadLine(string prefix = null)
    {
        _currentPrefix = prefix;
        _isReading = true;
        Console.Write(prefix);
        while (true)
        {
            ConsoleKeyInfo cki;

            cki = Console.ReadKey();

            if (cki.Key == ConsoleKey.Escape)
            {
                CurrentReadLine.Clear();
            }
            else if (cki.Key == ConsoleKey.Backspace)
            {
                if (CurrentReadLine.Length > 0)
                {
                    CurrentReadLine.Length--;
                }
                ClearConsole();
                Console.Write(prefix + CurrentReadLine);
            }
            else if (cki.Key == ConsoleKey.Enter)
            {
                Console.WriteLine();
                var result = CurrentReadLine.ToString();
                CurrentReadLine.Clear();
                _isReading = false;
                return result;
            }
            else
            {
                CurrentReadLine.Append(cki.KeyChar);
            }
        }
    }

    static void ClearConsole()
    {
        var length = Console.CursorLeft;
        Console.CursorLeft = 0;

        for (int i = 0; i <= length; i++)
        {
            Console.Write(" ");
        }
        Console.CursorLeft = 0;
    }

    public static void WriteLine(string format, params object[] args)
    {
        ClearConsole();

        Console.WriteLine(format, args);
        if (_isReading)
        {
            Console.Write(_currentPrefix + CurrentReadLine);
        }
    }
}
于 2014-02-27T07:36:45.733 回答
0

当您接受用户输入时,使用集中式记录器并禁用输出。

于 2012-09-06T12:26:38.080 回答
0

我知道这个问题主要是审美问题,但是这样的事情让我很恼火。我讨厌回答我自己的问题,但实际上我根据其他人的一些意见提出了一个相当不错的解决方案。我将附上一个代码示例,以防将来有人想做类似的事情。我知道可能有更好的方法来做到这一点,但这就是我所做的。

基本上,我没有在每个线程中使用 console.writeline,而是创建了一个新的子例程(TextOut),它背后有一些逻辑。我还更新了我的输入线程,将每个字符读入共享字符串,而不是使用 console.readline()。这是代码:

Private command As String = ""

Private Sub ConsoleInput()
    Dim cki As ConsoleKeyInfo
    cki = Console.ReadKey()
    If cki.Key = ConsoleKey.Escape Then
        command = "" 'Clear command
    ElseIf cki.Key = ConsoleKey.Backspace Then
        If Len(command) > 0 Then 'Make sure you don't go out of bounds
            For i As Integer = 0 To Len(command)
                Console.Write(" ") 'Clear output since new string will be shorter
            Next
            command = Left(command, Len(command) - 1) 'Shorten command by 1 char
        End If
        Console.CursorLeft = 0 'Set the cursor to the beginning of the line
        Console.Write(command) 'Write the command to the screen
    ElseIf cki.Key = ConsoleKey.Enter Then 'Command has been submitted, start checking
        Console.CursorLeft = 0 'Set the cursor to the beginning of the line
        For i As Integer = 0 To Len(command)
            Console.Write(" ") 'Clear output from command (hides the executed command)
        Next
        Dim tempCMD As String = command
        command = ""

        'If/then statements for validating command goes here

        command = "" 'Clear command to allow new input
    Else
        command += cki.KeyChar 'Add char to command string
        Console.CursorLeft = 0 'Set the cursor to the beginning of the line
        Console.Write(command) 'Write the command to the screen
    End If
    ConsoleInput() 'Loop for more input 
End Sub


Sub TextOut(ByVal message As String)
    If command <> "" Then
        For i As Integer = 0 To Len(command)
            Console.Write(" ") 'Clears output in case output is shorter than current command
        Next
        Console.CursorLeft = 0 'Sets cursor to beginning of row
    End If
    Console.WriteLine(message) 'Writes the current message to the screen
    If message <> command Then
        Console.Write(command) 'Writes the command back to the screen
    End If
End Sub
于 2012-09-06T14:05:32.187 回答