【问题标题】:Closing form with tasks running正在运行任务的关闭表单
【发布时间】:2012-09-13 23:30:41
【问题描述】:

我有一个 WinForms 应用程序,它由一个主 UI 线程和 4 个任务组成。我的主表单有一个私有成员级别变量,如下所示:

private bool keepThreadsRunning = false;

在我的主窗体的 Load() 事件中,我有以下内容:

keepThreadsRunning = true;
var task1Worker = Task.Factory.StartNew(() => DoStuff1());
var task2Worker = Task.Factory.StartNew(() => DoStuff2());
var task3Worker = Task.Factory.StartNew(() => DoStuff3());
var task4Worker = Task.Factory.StartNew(() => DoStuff4());

在我的每个 DoStuff() 方法中,我基本上都有这个:

while (keepThreadsRunning)
{
  // do work here
  Thread.Sleep(30000); // a couple of my tasks only need to run every 30 seconds or so
}

最后,在我的 Form_Closing() 事件处理程序中,我有以下内容:

keepThreadsRunning = false;
this.Close();

在任务管理器中查看我的应用程序,当我关闭表单时,似乎该过程正在结束,但我对这四个任务有点困惑。我对 this.Close() 的调用是否真的导致这些任务终止(即使它们在发生时在 Thread.Sleep() 调用中)?有没有比我现在编写代码更好的方法来实现这一点?

编辑 - 我已经简要地查看了任务取消(当我的应用程序退出时),但我的理解是我的任务需要定期检查取消令牌以确定它们是否已被取消。鉴于我的一些任务需要每 30 秒运行一次,我无法弄清楚如何实现 30 秒等待(当前是 Thread.Sleep())并且仍然让任务检查取消令牌。

【问题讨论】:

    标签: c# winforms task task-parallel-library


    【解决方案1】:

    当您关闭应用程序时,任务将相应关闭,因为任务是在线程池中的后台线程下处理的。因此,您无需定期检查取消令牌来确定它们是否已被取消

    【讨论】:

      【解决方案2】:

      首先,关闭主 UI 线程将终止您的其他任务。如果您需要它们继续运行,可以考虑在单独的控制台应用程序或 Windows 服务中运行它们。

      即使您在完成运行需要运行的方法时找到了延迟关闭表单的方法,这也只有在最终用户以您想要的方式关闭表单并且 Windows 是 Windows 的情况下才有效一百万零一种关闭应用程序的方法,因此无法保证这将起作用。

      对于每 x 秒异步运行一个方法,您可以只为整个事情使用一个计时器,如下所示:

      using System;
      using System.Timers;
      using System.Windows.Forms;
      
      namespace WindowsFormsApplication3
      {
          public partial class Form1 : Form
          {
              public Form1()
              {
                  InitializeComponent();
              }
      
              private void Form1_Load(object sender, EventArgs e)
              {
                  var timer1 = new System.Timers.Timer { Interval = 30000, Enabled = true };
                  var timer2 = new System.Timers.Timer { Interval = 20000, Enabled = true };
                  var timer3 = new System.Timers.Timer { Interval = 10000, Enabled = true };
                  var timer4 = new System.Timers.Timer { Interval = 5000, Enabled = true };
      
                  timer1.Elapsed += timer1_Elapsed;
                  timer2.Elapsed += timer2_Elapsed;
                  timer3.Elapsed += timer3_Elapsed;
                  timer4.Elapsed += timer4_Elapsed;
              }
      
              void timer4_Elapsed(object sender, ElapsedEventArgs e)
              {
                  //do work here
              }
      
              void timer3_Elapsed(object sender, ElapsedEventArgs e)
              {
                  //do work here
              }
      
              void timer2_Elapsed(object sender, ElapsedEventArgs e)
              {
                  //do work here
              }
      
              void timer1_Elapsed(object sender, ElapsedEventArgs e)
              {
                  //do work here
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        不要使用布尔值和Thread.Sleep(),而是使用WaitHandle,特别是ManualResetEvent,这样创建:

        var threadTerminationHandle = new ManualResetEvent(false);
        

        在你的帖子中:

        do {
          // do work here
        } while (!threadTerminationHandle.WaitOne(TimeSpan.FromSeconds(30))
        

        这将等到设置了 WaitHandle 或 30 秒后,以较早者为准。

        在您的表单中:

        threadTerminationHandle.Set();
        Close();
        

        【讨论】:

          猜你喜欢
          • 2015-06-24
          • 2013-06-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-22
          • 1970-01-01
          • 2013-01-25
          相关资源
          最近更新 更多