Состояние гонки с многопоточным консольным курсором в C#

Я был бы очень благодарен, если бы кто-нибудь мог указать мне правильное направление для решения этой проблемы, с которой я столкнулся в последние два дня: я работаю над программным обеспечением для ввода текста для консоли Visual Studio 2010 и мне нужно представлять время и иметь возможность одновременно вводить данные с клавиатуры.

Проблема в том, что метод ReadLine() позволяет мне писать только тогда, когда поток таймера спит, возможно, я мог бы решить это с помощью делегатов или расширенных методов продолжения задачи, я действительно немного запутался, поскольку это новые концепции. Вот код (извините за некрасивое форматирование):

using System;
using System.Threading.Tasks;
using System.Threading;


namespace Typing_Game_tests
{

    class Input_and_timer_multithread
    {
        public static void TimerThread() 
        { 
            for (int i = 1, mins = -1; i <= 1860; i++) 
            {
                Console.SetCursorPosition(0, 0);
                if (i % 60 == 1) 
                { 
                    mins++; 
                }

                Console.WriteLine("Timer: " + mins +  " minute(s) and " + i % 60 + " seconds elapsed");            

                Console.SetCursorPosition(0, 6); 
                Thread.Sleep(1000);
            }
        }


        static string keysRead;

        public static void GetInput()
        {
            while (Console.ReadKey(true).Key != ConsoleKey.Enter)
            {
                Console.SetCursorPosition(0, 6);
                keysRead = Console.ReadLine();
                Console.SetCursorPosition(0, 6);
            }

            Console.SetCursorPosition(0, 6);
            Console.WriteLine(keysRead);

        }

        static void Main(string[] args)
        {               
            Thread timerMain = new Thread(new ThreadStart(TimerThread));
            timerMain.Start();
            Task getinputMain = new Task(GetInput);
            getinputMain.Start();
        }
     }
  }

В большинстве примеров, которые я нашел и изучил, есть лямбда-выражения и делегаты, которые являются новыми темами, которые мне все еще трудно понять и, следовательно, реализовать. Было бы проще, если бы я мог остановить основной поток таймера, пока ReadLine() не выполнит свою работу, но это не имело бы смысла; плюс я пытался использовать пространство имен Timers, но у меня была та же проблема.

Я хотел создать впечатление, что курсор постоянно получает ввод от пользователя, но я до сих пор не нашел способа эффективно переключать потоки назад и вперед, не блокируя их. Спасибо.


person User567845    schedule 18.06.2013    source источник


Ответы (1)


Этот вид кода больше не работает, начиная с .NET 4.5. Console.ReadKey() и ReadLine() устанавливают блокировку, не позволяющую другим потокам выполнять запись в консоль. Вам нужно будет заменить его, чтобы замок больше нельзя было взять. Для этого требуется опрос клавиатуры с помощью Console.KeyAvailable. Вот упрощенный метод замены для Console.ReadLine():

static object ConsoleLock = new object();

static string ReadString() {
    var buf = new StringBuilder();
    for (; ; ) {
        while (!Console.KeyAvailable) System.Threading.Thread.Sleep(31);
        lock(ConsoleLock) {
            var key = Console.ReadKey(true);
            if (key.Key == ConsoleKey.Enter) {
                Console.WriteLine();
                return buf.ToString();
            }
            else if (key.Key == ConsoleKey.Backspace) {
                if (buf.Length > 0) {
                    buf.Remove(buf.Length - 1, 1);
                    Console.Write("\b \b");
                }
            }
            else {
                buf.Append(key.KeyChar);
                Console.Write(key.KeyChar);
            }
        }
    }
}

Также заблокируйте ConsoleLock в другом потоке, который перемещает курсор, поэтому вывод строго разделен.

person Hans Passant    schedule 18.06.2013
comment
+1 Приятно узнать о внутренней блокировке в ReadKey(), спасибо. - person xxbbcc; 18.06.2013
comment
Спасибо, что поделились своим опытом, я следовал вашим указаниям, но в итоге все зашло в тупик, мне придется вернуться к этому проекту через пару месяцев. Грейзи. - person User567845; 19.06.2013