2011-10-20 158 views
5

在我正在开发的一个应用程序中,我有一个简单的坐在那里并显示日志数据的主窗体,以及一个自主完成循环工作的工作线程。优雅地关闭了一个线程

MyWorker worker = new MyWorker(); 
MainForm mainForm = new MainForm(); 

// Subscribe form to log event so log data gets displayed 
worker.Log += mainForm.Log; 

// Start the worker thread's MainLoop 
new Thread(new ThreadStart(worker.MainLoop)).Start(); 

// Show the form (blocking) 
Application.Run(mainForm); 

// If we end up here, the form has been closed and the worker has to stop running    
worker.Running = false; 

正如您所看到的,只要窗体关闭,工作线程应该停止。这名工人是这样的:

public class MyWorker 
{ 
    public String Running { get; set; } 

    public MyWorker() 
    { 
     Running = true; 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 

      DoExtensiveWork1(); 
      if (!Running) return; 

      DoExtensiveWork2(); 
      if (!Running) return; 

      DoExtensiveWork3(); 
      if (!Running) return; 

      DoExtensiveWork4(); 
      if (!Running) return; 

      DoExtensiveWork5();   
      if (!Running) return; 

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      for (int i = 0; i < 900; i++) 
      { 
       Thread.Sleep(1000); 
       if (!Running) return; 
      } 
     } 
    } 
} 

正如你所看到的,我想线程能够连续工作的操作之间切换时停止,而不是在一个操作中。当一个操作(DoExtensiveWorkN)完成时,其状态和结果将保留在磁盘或数据库中,因此在操作正在进行时退出(例如,通过Thread.Abort)不是选项。

但是,我发现这段代码我刚刚写了一些令人厌恶的代码,特别是睡眠时间为900秒的“等待循环”,以防止线程在检测到Running已被设置为false

我宁愿能够抛出某种事件来完成一项工作后立即停止主循环。

任何人都可以指出我正确的方向如何做到这一点,或者如果因为我完全误解了线程而需要全部重写,请告诉我某处解释这些原则的地方?

回答

13

您可以清理各个任务的运行和15分钟等待循环。

我建议也许使用这样的事情:

public class MyWorker 
{ 
    private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false); 
    private readonly Action[] _workUnits; 

    private bool Running 
    { 
     get { return !_stopEvent.WaitOne(0); } 
    } 

    public MyWorker() 
    { 
     _workUnits = new Action[] 
     { 
      DoExtensiveWork1, 
      DoExtensiveWork2, 
      DoExtensiveWork3, 
      DoExtensiveWork4, 
      DoExtensiveWork5 
     }; 
    } 

    public void Stop() 
    { 
     _stopEvent.Set(); 
    } 

    public void MainLoop() 
    { 

     while (Running) 
     { 
      foreach (var workUnit in _workUnits) 
      { 
       workUnit(); 
       if (!Running) return; 
      }   

      // We have to wait fifteen minutes (900 seconds) 
      // before another "run" can be processed 
      if (_stopEvent.WaitOne(900000)) return; 
     } 
    } 
} 

然后在下一个合适的点停止进程:

Worker.Stop(); 
0

我建议使用System.Timers.Timer

你可以用跑步的东西做你的工作,而不是使用睡眠,你可以设置定时器在15分钟内再次关闭。

如果您想尽早停止它,请调用某种终止方法(类似于设置您的Running = true变量)来停止计时器。

应该指出的是,每当计时器事件触发时,它将启动一个新的线程,所以你不需要担心杀死后台线程。您的线程完成其运行处理,将计时器设置为在15分钟内运行,然后线程自然结束。如果您在等待期间中止,那么您只需摆脱计时器,无需进行更多清理。如果在运行过​​程中中止,则让运行结束,并在结束时检查一个标志,并且不会再次启动计时器,然后线程结束。

对于计时器,您需要将计时器设置为在过程结束时手动启动。另一种方法是让计时器每15分钟计时一次,但这意味着如果您的处理花费了10分钟,那么它只能在下一次运行之前花5分钟。如果超过15分钟,您可能会遇到麻烦。还手动重新启动计时器可确保在另一个正在运行时处理不应重新启动。

+0

定时器不会解决我遇到的主要问题:我不希望工作在表单关闭时继续进行,并且我不想添加大量代码来检查该问题。 – CodeCaster

+0

@CodeCaster:啊,我误解了“在操作正在进行时退出(例如,通过Thread.Abort)不是一个选项”来表示整个线程。我的想法然后会使用一系列的行动,但铱星击败了我。尽管如此,我仍然更喜欢15分钟的计时器。 :) – Chris

+0

我可以更清楚地描述。 (还是)感谢你的建议。 :-) – CodeCaster