如何在 C# 的控制台应用程序中使用键盘输入停止正在运行的方法?

Posted

技术标签:

【中文标题】如何在 C# 的控制台应用程序中使用键盘输入停止正在运行的方法?【英文标题】:How to stop a running method with keyboard input in a Console Application on C#? 【发布时间】:2011-07-01 09:33:04 【问题描述】:

简而言之,我正在使用 C# 进行科学计算,并且我编写了一个方法,它有一个 while 循环,可以运行到用户指定数量的步骤...实际上,这个方法可能需要很长时间才能执行(比如超过 5 个小时)。例如,当需要这么长时间时,我可能想停止按 Esc 键的方法。

当我读到一些关于破坏while 的内容时,它就像Boolean 标志或类似的东西一样简单。所以我想到了这样的事情:

public Double? Run(int n)

    int i = 0;
    while ((i < n) && (/* inputkey != ConsoleKey.Escape */))
    
        // here goes the heavy computation thing
        // and I need to read some "inputkey" as well to break this loop
        i++;
    
    // I'm not worried about the return statement, as it is easy to do...
    // returns null if the user skipped the method by pressing Escape
    // returns null if the method didn't converged
    // returns the double value that the method calculated otherwise

嗯,这就是我到现在为止一直想知道的......那么,请你提供一些有用的想法吗?我如何等待用户输入(我考虑过Events,但我不确定如何在这里实现它,我认为如果我必须每隔一段时间听一个键,它会使代码变得更慢代码进入的步骤...

嗯,有什么想法或方法吗?


更新:我想我应该更好地描述这个问题。您给我的所有解决方案都可能解决我提出的这个问题,但我认为我对我的实际问题并不完全可靠。我不知道我是应该问另一个问题还是继续这个问题......

【问题讨论】:

【参考方案1】:

如果您只是想停止应用程序,可以使用命令行中的 Ctrl-C 来完成。如果您确实需要在长时间运行的过程中截取输入,您可能希望生成一个工作线程来执行长时间运行的过程,然后只需使用主线程与控制台交互(即 Console.ReadLine())。

【讨论】:

我同意,看看BackgroundWorker:msdn.microsoft.com/en-us/library/… 同意。 Background Worker 是最好的方法。您甚至可以生成多个线程,这样您的“长时间运行的进程”就可以少一些运行时间。 好吧,我已经将 BackgroundWorker 用于我构建的 GUI 应用程序,但我不知道我也可以在控制台应用程序中使用它。现在,关于您的第一点,我不能只使用 CTRL C,因为这只是我所拥有的一个简单示例......实际上我运行了 Run() 方法数十次并使用返回值执行平均值,我对各种参数集执行此操作,打算在程序末尾写入一个文件...我的意思是,如果我按 CTRL C,程序肯定会停止,不仅会跳过一个值...你明白吗?感谢bg工作者的提示!【参考方案2】:

您可以从一个单独的线程运行此方法,并在按下某个键时设置一个停止变量:

object myLock = new object();
bool stopProcessing = false;

public Double? Run(int n)

    int i = 0;
    while (i < n)
    
        lock(myLock)
        
            if(stopProcessing)
                break;
        
        // here goes the heavy computation thing
        // and I need to read some "inputkey" as well to break this loop
        i++;
    

当一个键被按下时,相应地更新 stopProcessing:

Console.ReadKey();
lock(myLock)

    stopProcessing = true;

【讨论】:

我认为你的想法或多或少是杰夫的想法,不是吗?谢谢你们!【参考方案3】:

您需要使用线程来执行此操作。启动任务时,生成一个新线程并在该线程上执行任务。然后在您的 Program.cs 中,等待用户输入。如果用户输入了一些有意义的东西 - 在你的情况下,Esc 键 - 提醒后台线程该操作。最简单的方法是设置一个静态变量。后台线程将检查这个静态变量,当它被更改时,后台线程将自行清理并中止。

请参阅MSDN article on Threading。

代码示例会更深入一点,但它看起来像这样:

public class Program.cs

    public static myFlag = false;
    public void Main()
    
        thread = new Thread(new ThreadStart(DoWork));
        thread.Start();
        Console.ReadLine();
        myFlag = true;
    
    public static DoWork()
    
        while(myFlag == false)
        
            DoMoreWork();
        
        CleanUp()
    
    public static DoMoreWork()  
    public static CleanUp()  


【讨论】:

我认为你的想法或多或少是马克的想法,不是吗?谢谢你们! @Jeff:不过要小心;如果没有锁定,这不是线程安全的(你会遇到竞争条件)。 @Girardi 是的,我们提出了几乎相同的建议。 @Mark 你是对的。我只是想为 Girardi 提供一个样品,我并没有打算模拟整个 9 码。此外,在观众有限的情况下,情况听起来很简单,所以比赛条件可能是一个极端情况。尽管如此,锁定绝对是正确的方法。 @Jeff:我只是想指出一点;实际上,我之前已经删除了我的一个帖子,因为我没有完全线程安全的例子,所以我学会了明确:-)【参考方案4】:

及时在Console.KeyAvailable上池并采取相应措施。

【讨论】:

【参考方案5】:
using System;
using System.Threading.Tasks;

namespace ConsoleApplication4

    class Program
    
        static bool _cancelled = false;
        static void Main( string[] args )
        
            var computationTask = Task.Factory.StartNew(PerformIncredibleComputation);
            var acceptCancelKey = Task.Factory.StartNew(AcceptCancel);


        while (!acceptCancelKey.IsCompleted && ! computationTask.IsCompleted)
        

            computationTask.Wait (100);        
        

        if( acceptCancelKey.IsCompleted && !computationTask.IsCompleted )
        
            computationTask.Wait (new System.Threading.CancellationToken ());
        
        else if(!acceptCancelKey.IsCompleted)
        
            acceptCancelKey.Wait(new System.Threading.CancellationToken());
        


    


    private static void PerformIncredibleComputation()
    
        Console.WriteLine("Performing computation.");
        int ticks = Environment.TickCount;
        int diff = Environment.TickCount - ticks;
        while (!_cancelled && diff < 10000)
        
           //computing  
        
        Console.WriteLine("Computation finished");
    

    private static void AcceptCancel()
    

        var key = Console.ReadKey(true);
        Console.WriteLine("Press Esc to cancel");
        while(key.Key != ConsoleKey.Escape)
        
            key = Console.ReadKey(true);
        
        _cancelled = true;
        Console.Write("Computation was cancelled");

    

【讨论】:

以上是关于如何在 C# 的控制台应用程序中使用键盘输入停止正在运行的方法?的主要内容,如果未能解决你的问题,请参考以下文章

编写一个C#控制台应用程序,从键盘输入年份,判断此年份是否闰年。

C#如何全局禁用键盘的回车键

如何在c#控制台应用程序中自动(强制)停止当前线程[重复]

C# - 向子进程发送输入 - 控制台应用程序

C# 如何编写程序监控键盘,即使程序的窗体不是当前活动窗体

如何从 C# 控制台停止办公应用程序 [重复]