4

最近、VB.Netを使用してマルチスレッドコンソールベースのアプリケーションの作業を開始しましたが、(説明可能な)エラーが発生しています。プログラム自体は、さまざまなスレッドの外部アプリからソケット接続を受信するサーバーです。また、ユーザーがconsole.readlineを使用してアプリにテキストを入力するのを待つだけの別のスレッドもあります(これにより、HELPやSTATUSなどのサーバーコマンドを入力できます)。

問題は、プログラムがさまざまなソケットスレッドからメッセージを出力することです。コマンドを入力している最中に、コマンドが分割されてしまいます。コマンド「status」を入力すると、正常に機能しますが、コマンドを入力したユーザーにとっては、コマンドが分割され、コマンドが間違って入力されているかどうかを確認できない場合があります。例えば:

staNewクライアントが接続されました。
tus

なぜこれが起こっているのかは知っていますが、これを停止する簡単な修正(入力中に他のスレッドを一時停止する必要はありません)、または移動しないコンソールコマンドを入力できるようにする簡単な方法があるかどうか興味があります追加のアプリ出力。

4

5 に答える 5

1

いくつかの提案:

  • コンソールに直接書き込むのではなく、キューにメッセージを書き込みます。ユーザーがコマンドを入力したら、最近のすべてのメッセージをキューから取り出し、コンソールにダンプします。
  • 古いBBSスタイルのチャットUIなど、コンソールの別の領域にメッセージを出力します。ページの1つのセクションをメッセージ用に、別のセクションを入力とユーザーコマンドの結果用に用意します。それには、コンソール側でもう少し作業が必要です。
  • メッセージを別のログファイルに出力します。ユーザーはtail、または同様のアプリケーションを使用してフォローできます。ユーザーは、メッセージを表示するアプリと、コマンドをインタラクティブに実行して出力を表示するアプリの2つのアプリを同時に開くことができます。
于 2012-09-06T12:36:49.137 に答える
1

Cシャープ相当

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

これは、readlineのプレフィックスをサポートし、再利用するために独自のクラスにパッケージ化された、わずかに異なるバージョンです。また、readlineに文字列の代わりにStringBuilderを使用します。

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 に答える