Приостановка текущего потока через пользовательский ввод в консольном окне приложения - PullRequest
0 голосов
/ 22 января 2012

Это решение, которое я придумал, чтобы приостановить / возобновить активный поток внутри консоли ...

Для ввода требуется второй поток, на который реагирует и основной цикл. Но мое незнание C # заставляет меня задуматься, есть ли более простое решение? Возможно, это можно сделать с помощью одного основного потока?

По сути, я тестирую несколько вещей, которые будут повторяться с заданным FPS, и я хочу иметь возможность приостановить и возобновить итерацию с помощью ввода с клавиатуры. Любые отзывы приветствуются.

class TestLoops
{
    int targetMainFPS = 5;
    int targetInputFPS = 3;
    bool CONTINUE = true;

    Thread testLoop, testLoop2;
    ConsoleKeyInfo cki;

    ManualResetEvent resetThread = new ManualResetEvent(true); //How to correctly pause threads in C#.
        public void Resume() { resetThread.Set(); }
        public void Pause() { resetThread.Reset(); }

    public TestLoops()
    {
        //_start = DateTime.Now.Ticks;
        Console.Write("CreatingLoop...");

        this.testLoop = new Thread(MainLoop);
        this.testLoop.Start();

        this.testLoop2 = new Thread(InputLoop);
        this.testLoop2.Start();
    }

    void MainLoop()
    {
        long _current = 0;
        long _last = 0;

        Console.Write("MainLoopStarted ");
        while(CONTINUE)
        {
            resetThread.WaitOne();

            _current = DateTime.Now.Ticks / 1000;
            if(_current > _last + (1000 / targetMainFPS) )
            {
                _last = _current;

                //Do something...
                Console.Write(".");

            }
            else
            {
                System.Threading.Thread.Sleep(10);
            }
        }
    }

    void InputLoop()
    {
        long _current = 0;
        long _last = 0;

        Console.Write("InputLoopStarted ");
        while(CONTINUE)
        {
            _current = DateTime.Now.Ticks / 1000;
            if(_current > _last + (1000 / targetInputFPS))
            {
                _last = _current;

                //Manage keyboard Input
                this.cki = Console.ReadKey(true);
                //Console.Write(":");
                if(this.cki.Key == ConsoleKey.Q)
                {
                    //MessageBox.Show("'Q' was pressed.");
                    CONTINUE = false;
                }

                if(this.cki.Key == ConsoleKey.P)
                {
                    this.Pause();
                }
                if(this.cki.Key == ConsoleKey.R)
                {
                    this.Resume();
                }
            }
            else
            {
                System.Threading.Thread.Sleep(10);
            }
        }
    }

    public static void Main(string[] args)
    {
        TestLoops test = new TestLoops();
    }

}

Ответы [ 2 ]

3 голосов
/ 22 января 2012

Вы можете существенно упростить его, используя неблокирующую проверку Console.KeyAvailable.Таким образом, вы можете выполнить весь свой код из основного потока:

using System;
using System.Threading;

class TestLoops
{
    const int targetFPS = 5;

    public static void Main(string[] args)
    {
        bool _continue = true;
        DateTime _last = DateTime.MinValue;

        while (_continue)
        {
            if (Console.KeyAvailable)
            {
                ConsoleKeyInfo cki = Console.ReadKey(true);

                if (cki.Key == ConsoleKey.Q)
                {
                    Console.WriteLine("\n'Q' was pressed.");
                    _continue = false;
                }

                if (cki.Key == ConsoleKey.P)
                {
                    Console.WriteLine("\n'P' was pressed; press 'R' to resume.");

                    // Block until 'R' is pressed.
                    while (Console.ReadKey(true).Key != ConsoleKey.R)
                        ; // Do nothing.

                    Console.WriteLine("'R' was pressed.");
                }
            }

            DateTime _current = DateTime.Now;
            if (_current - _last > TimeSpan.FromMilliseconds(1000F / targetFPS))
            {
                _last = _current;

                // Do something...
                Console.Write(".");
            }
            else
            {
                System.Threading.Thread.Sleep(10);
            }
        }        
    }
}

Редактировать : Ответ на комментарий.

Будьте осторожны.Код ниже блокирует из-за вызова Console.ReadKey, а не из-за цикла while.Цикл while существует только для того, чтобы, если пользователь вводит символ, отличный от R, программа отбрасывает его и ожидает ввода другого символа.

// Block until 'R' is pressed.
while (Console.ReadKey(true).Key != ConsoleKey.R)
    ; // Do nothing.

Если вы хотитеблокировать до тех пор, пока не будет нажата любой символ , просто введите:

Console.ReadKey(true);
0 голосов
/ 22 января 2012

Это будет работать, да.Единственное, что вы можете рассмотреть, это убедиться, что рабочий поток останавливается, даже если он был ранее приостановлен, когда вы нажимаете клавишу Q:

if(this.cki.Key == ConsoleKey.Q)
{
     CONTINUE = false;
     this.Resume(); // <-- make sure that the worker thread resumes
}

Как примечание, обычно поддерживается постоянный FPS пропуская фреймы в зависимости от ситуации, вместо того, чтобы останавливать поток.Приостановка потока является неточной и не позволяет точно настроить скорость анимации.Thread.Sleep также может вести себя по-разному на разных аппаратных средствах и может иметь довольно плохое разрешение (минимальный временной интервал ОС составляет около ~ 16 мс IIRC).

...