【问题标题】:How to stop a running method with keyboard input in a Console Application on C#?如何在 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,但我不确定如何在这里实现它,我认为如果我必须每隔一段时间听一个键,它会使代码变得更慢代码进入的步骤...

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


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

【问题讨论】:

    标签: c# input while-loop user-input break


    【解决方案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");
      
          }
      }
      

      }

      【讨论】:

        猜你喜欢
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 2020-01-21
        • 1970-01-01
        • 1970-01-01
        • 2010-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多