防止我的控制台应用程序在.NET计时器启动后关闭

5

我有一个简单的代码:

static void Main(string[] args)
  {
      var timer = new Timer(0.5 * 60 * 1000); // 0.5 minutes times 60 seconds times 1000 miliseconds
      timer.Elapsed += new System.Timers.ElapsedEventHandler(Start);
      timer.AutoReset = true;            
      timer.Start();
  }

这将启动一个计时器并启动整个程序逻辑。问题是控制台应用程序会关闭,什么也不会发生。我该如何解决这个问题?我知道如果像while(true)这样做就可以解决,但这似乎不是一个优雅/适当的解决方法。


它会关闭因为timer.Start()方法会触发一个新线程。一旦你调用它,它就不会阻止当前(主)线程。如果你在等待计时器完成,那么你可能想要使用回调函数。 - Brandon
1
“not closing”是什么意思?会永久存在吗?只需要使用Console.ReadLine()即可。 - Peter Ritchie
@Damieh 为什么不先睡觉,然后直接调用你的“开始(Start)”呢? - crashmstr
5个回答

9
也许尝试一下:

Console.Read(); 

代码结尾处
通过这种方式,只要用户没有按键,控制台窗口就不会关闭。

3

最后一条回答是7年前的。在此期间,System.Threading中的Timer、Sleep等方法不应再使用了。它们存在死锁的风险,我必须警告特别是在ASP.NET Core中不要使用它们。

需要考虑几个问题:

  • 没有程序可以永远运行。当程序应该关闭(通过关闭或信号)时会发生什么?
  • 终止必须在您的程序逻辑中实现。许多异步.NET Core方法支持CancellationToken作为参数。

有两种方法可以创建一个“无限”程序:

  • .NET Core中的Worker Services是编写应该作为服务永久运行的程序的常见方法。它们可以注册为Windows服务。 有关更多信息,请参见learn.microsoft.comdevblogs.microsoft.com(如果您想将服务注册为“真正的”服务)。
  • 如果您需要一个在前台执行任务的“真正”控制台应用程序,则可以使用以下示例代码完成:
using System;
using System.Threading.Tasks;
using System.Threading;

namespace DemoApp
{
    internal class Program
    {
        private static int _isRunning = 0;

        private static async Task Main(string[] args)
        {
            // No program can run infinitely. We always have to provide a mechanism for termination.
            var tcs = new CancellationTokenSource();
            var periodTimeSpan = TimeSpan.FromSeconds(10);

            // This mechanism is CTRL+C, so we catch this.
            Console.CancelKeyPress += (sender, e) =>
            {
                tcs.Cancel();
                e.Cancel = true;
            };

            try
            {
                // No endless loop. We are checking for a cancellation request!
                while (!tcs.IsCancellationRequested)
                {
                    // Perform your work.
                    var task1 = Run(tcs.Token);
                    var task2 = Task.Delay(periodTimeSpan, tcs.Token);
                    // If the execution time of Run is greater than periodTimeSpan, we will wait. Depends on your needs.
                    await Task.WhenAll(task1, task2);
                }
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("User canceled your app.");
            }
        }

        private static async Task Run(CancellationToken token)
        {
            // Should never occur if you use WhenAll()
            if (Interlocked.Exchange(ref _isRunning, 1) == 0)
            {
                // Perform your work.
                _isRunning = 0;
            }
        }
    }
}

嗨,想知道为什么需要这个 ask.Delay var task2 = Task.Delay(periodTimeSpan, tcs.Token); 吗? - Mist
模拟一个计时器。例如:如果您定期从API获取数据,则服务器将在没有此延迟的情况下感到非常不满意 ;) - Michael

1
添加一个 Console.ReadKey(); 这将允许您通过按任意键关闭控制台窗口。
    static void Main(string[] args)
    {
        var timer = new Timer(0.5 * 60 * 1000); // 0.5 minutes times 60 seconds times 1000 miliseconds
        timer.Elapsed += new System.Timers.ElapsedEventHandler(Start);
        timer.AutoReset = true;            
        timer.Start();

        Console.ReadKey();
    }

1
如果你只想运行一个计时器并等待,那么Console.Read()是你的好帮手。
你的代码之所以终止是因为该函数初始化计时器,启动它,然后……到达Main函数的结尾。结果,函数退出了。
除了启动一个被忽略的计时器之外,当前代码没有做任何有用的事情。
如果你想看到计时器起作用,可以在启动计时器后做一些其他工作。然后,在一段时间后停止计时器,并按照自己的意愿输出/评估它。(计时器不在主线程上运行,而像Console.Read()这样的东西将在主线程上运行,因此会阻塞线程,直到你输入为止)。

0

你的控制台应用程序不会因为计时器启动而结束,它会在程序执行完 Main() 方法后结束。

无论你采取什么措施来防止程序退出 Main() 方法都是有帮助的。虽然许多提出的解决方案实际上都可以做到这一点,但我想强调的是,问题并不是由计时器引起的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接