【问题标题】:Application.Run(), Application.Exit() and timersApplication.Run()、Application.Exit() 和计时器
【发布时间】:2012-10-23 03:15:16
【问题描述】:

我有一个 winforms 应用程序,我将通过任务调度程序调用它来下载文件并将它们插入数据库。但是文件何时可用是不可预测的,所以我使用计时器 (System.Windws.Forms.Timer) 来轮询文件。

 static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            if (args != null && args.Length > 0)
            {

                Form1 f1 = new Form1();
                if (f1.IsLive)
                {
                    f1.OnLaunch(); // tests DB connection and reads and updates the expiry date list.

                    if (args[0] == "FullStats")
                        f1.FullStatsDataPump();
                    else if (args[0] == "OptionsStats")
                    {
                        f1.OptionsStatsTimer_Tick(null, null);
                        f1.OptionsStatsTimer.Start();
                    }
                    else if (args[0] == "OptionsTraded")
                    {
                        f1._optionsTradedTimer_Tick(null, null);
                        f1.OptionsTradedTimer.Start();
                    }
                    else
                    {
                        EmailManager.InvalidInputArgument(args[0]);
                        Application.Exit();
                    }

                    Application.Run();
                }

            }

在上面的代码 sn-p 中,OptionsTradedTimer / OptionsStatsTimer 都轮询文件,然后开始以Application.Exit() 结尾的进程。这运行得很好,但之后它陷入了无限的消息循环。我认为Application.Run() 将在第一个计时器滴答后立即被调用,因此当Application.Exit() 最终被调用时,它将结束消息循环。但是,如果我单步执行代码,那么在Application.Exit() 之后,程序将返回到它起源的 timer.tick(),然后继续到底部的Application.Run()。底部的Application.Run() 是必需的,因为没有它,计时器只会滴答一次,然后应用程序将退出。

那么我如何正确地告诉应用程序退出呢?或者我应该在哪里打电话给Application.Run()

【问题讨论】:

  • Application.Exit() 不会导致程序结束。当 Main() 返回时程序结束。 msdn.microsoft.com/en-us/library/… Application.Run() 创建一个消息循环(一个无限循环),在 Application.Exit() 被调用时终止。它不起作用的原因是代码没有逻辑意义。因此,您需要重新设计您的应用程序。
  • 问题已解决 - 我需要做的就是确保计时器滴答方法在第一次滴答时没有调用 Application.Exit()。我将在星期一删除这个问题,因为我看不出这个解决方案对其他人有什么用处。谢谢大家。
  • 听起来不错。我仍然认为你想修改你的设计。 :-)

标签: c# timer scheduled-tasks message-loop


【解决方案1】:

这听起来有点奇怪——也许你应该简单地将 Application.exit 移动到一个单独的过程中,如果你需要退出程序,只需更改一些布尔变量。在您的主程序中,您可以等待布尔值更改然后退出程序一次。此外,您可以在开始时将布尔变量设置为 false,这样您就不需要运行了。

【讨论】:

    【解决方案2】:

    我认为您不需要使用 Application.Exit()。您的 Main() 方法是应用程序的入口点;当它返回时,应用程序退出。一般来说,Application.Run() 用于启动一个一直运行到关闭的 Windows 窗体。试试这个代码 - 我没有测试过它,但它应该做你想要的。

    using System.Linq;
    using System.Threading;
    
    class MyApplication
    {
        [STAThread]
        static void Main(string[] args)
        {
            const string ARG_SHOWFORM = "ShowForm";
            const string ARG_STATS = "OptionsStats";
            const string ARG_TRADED = "OptionsTraded";
    
            if (args.Contains(ARG_SHOWFORM) || args.Length == 0) {
                Application.Run(new Form1());  //This will block until the form is closed.
    
                //Make sure all your supporting logic is placed into the Form.Loaded event on the form (i.e.
                //get it out of the Main() method).
    
                return;
            }
    
            if (args.Contains(ARG_STATS))
                OptionsStatsMethod();
    
            else if (args.Contains(ARG_TRADED))
                OptionsTradedMethod();
    
            else
                EmailManager.InvalidInputArgument(args[0]);
    
        }
    
        private void OptionsTradedMethod()
        {
            while (true) {
                if (downloadSuccessful) //Use a method here that returns a boolean if the download succeeded.
                    break;
                else
                    Thread.Sleep(DEFAULT_WAIT_TIME_MS);
            }
        }
    
        private void OptionsStatsMethod()
        {
            while (true) {
                if (downloadSuccessful)  //Use a method here that returns a boolean if the download succeeded.
                    break;
                else
                    Thread.Sleep(DEFAULT_WAIT_TIME_MS);
            }
        }
    }
    

    【讨论】:

    • 这永远不会退出,因为使用任务调度程序时表单不会显示。该表单只是提供了一个很好的接口来测试和调试。
    • 如何在程序中添加一个选项以通过命令行显示表单?就像我说的,我认为这需要重新设计,让它做你想做的事。
    • 我不希望表单显示,而且我认为在通过调度程序运行时它不会显示。该表单用于我通过 VS 运行它时,但我还需要每天通过任务调度程序运行它。我不明白如何摆脱计时器?对我来说,最明智的重新设计是让它成为 Windows 服务,但我真的不想这样做,我很确定这可以很容易地修复。只是了解计时器和 application.run() 之间的时间问题
    • 我将为您编写一组伪代码,以便您了解我在说什么。
    【解决方案3】:

    为了让你的 main 方法保持运行(保持它的线程活跃),你可以使用 Thread.Sleep 而不是 Application.Run()。试试这个:

    Thread.Sleep(System.Threading.Timeout.Infinite);
    

    而不是 Application.Exit() 使用:

    Process.GetCurrentProcess().Kill();
    

    这种方式可以工作,但请考虑为此使用 Windows 服务。

    【讨论】:

    • 谢谢。那么这是否意味着计时器将继续触发?还有我如何告诉程序在我想要的时候结束?
    • 如果在 Main() 线程上使用 Thread.Sleep,会导致程序挂起。
    • 当有其他线程在运行时它不会挂起(这里是定时器)。请试一试。
    【解决方案4】:

    无法删除问题,不妨回答一下。

    仅当计时器第一次运行时到达Application.Exit() 行时才会出现此问题。 (即,如果该文件在程序运行时已经可用)。在这种情况下,Application.Exit()Application.Run() 之前被调用,如果计时器在第一次运行时没有到达 Application.Exit()(即文件尚不可用),那么 Application.Run() 被调用,Application.Exit() 只被调用稍后。

    所以为了解决这个问题,我只是在计时器的刻度方法中添加了一个条件,以确保它们在第一次运行时不会发生任何事情。

    我不同意按照它的方式重组程序带有用于测试、调试和在出现问题时下载文件的按钮的应用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-10
      • 1970-01-01
      • 2016-03-28
      • 2011-01-19
      • 1970-01-01
      • 2022-12-03
      相关资源
      最近更新 更多