在控制台窗口应用程序中通过用户输入暂停当前线程

发布于 2024-12-28 06:30:49 字数 2496 浏览 3 评论 0原文

这是我想出的用于暂停/恢复控制台内活动线程的解决方案......

它需要第二个线程进行输入,主循环也会做出反应。但我对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();
    }

}

This is the solution I came up with to pause/resume an active thread inside a console...

It requires a second thread for input, which the main loop reacts too. But my ignorance of C# makes me wonder if there is a simpler solution? Perhaps one that can be done with the main thread alone?

Essentially, I am testing several things that will iterate at the defined FPS, and I want to be able to pause and resume the iteration via keyboard input. Any feedback is appreciated.

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();
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

海螺姑娘 2025-01-04 06:30:49

您可以使用非阻塞 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);

You can simplify it drastically by using the non-blocking Console.KeyAvailable check. That way, you can execute all your code from the main thread:

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);
            }
        }        
    }
}

Edit: Replying to comment.

Be careful. The code below blocks due to the Console.ReadKey call, not due to the while loop. The while loop is there only so that, if the user enters a character other than R, the program would discard it and wait for another character to be entered.

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

If you wanted to block until any character is pressed, you would simply use:

Console.ReadKey(true);
北音执念 2025-01-04 06:30:49

这会起作用,是的。您唯一可以考虑的事情是确保工作线程停止,即使它之前已暂停,当您按 Q 键时:

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

作为旁注,恒定的 FPS 通常由 维持适当地跳过帧,而不是暂停线程。暂停线程是不精确的,并且不允许微调动画速度。 Thread.Sleep 在不同硬件上的表现也可能不同,并且分辨率可能相当差(最小操作系统时间片约为 16ms IIRC)。

This will work, yes. The only thing that you could consider is making sure that the worker thread stops even if it was previously suspended, when you press the Q key:

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

As a side note, constant FPS is usually maintained by skipping frames as appropriate, rather than pausing a thread. Pausing a thread is imprecise, and doesn't allow fine tuning animation speeds. Thread.Sleep can also behave differently on different hardware, and can have rather poor resolutions (a minimum OS timeslice is around ~16ms IIRC).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文