如何在c#中正确终止工作线程

问题陈述

我有一个工作线程,基本上扫描文件夹,进入其中的文件,然后睡了一会儿。 扫描操作可能需要2-3秒,但不会多。 我正在寻找一种方法来优雅地阻止这个线程。

澄清 :我想在线程处于hibernate状态时停止线程,而不是在扫描时停止线程。 但是,问题是我不知道线程的当前状态是什么。 如果它正在睡觉我想让它立即退出。 如果正在扫描,我希望它在尝试阻止的那一刻退出。

尝试解决方案

起初我使用的是睡眠和中断。 然后我发现Interrupt并没有真正打断Sleep – 它只能在线程TRIES进入hibernate状态时才有效。

所以我切换到Monitor Wait&Pulse。 然后我发现Pulse只在我实际上在等待时才起作用。 所以现在我有一个看起来像这样的线程:

while (m_shouldRun) { try { DoSomethingThatTakesSeveralSeconds(); lock (this) { Monitor.Wait(this, 5000); } } catch (ThreadInterruptedException) { m_shouldRun = false; } } 

现在我需要制作我的Stopfunction。 所以我开始:

 public void Stop() { m_shouldRun = false; lock (this) { Monitor.Pulse(this); } thread.Join(); } 

但这不起作用,因为我可能在线程工作时发出脉冲(虽然它没有等待)。 所以我添加了Interrupt:

 public void Stop() { m_shouldRun = false; thread.Interrupt(); lock (this) { Monitor.Pulse(this); } thread.Join(); } 

另一种选择是使用:

 public void Stop() { m_shouldRun = false; while (!thread.Join(1000)) { lock (this) { Monitor.Pulse(this); } } } 

这个问题

什么是首选方法? 有第三种方法更可取吗?

另一种方法是使用事件:

 private ManualResetEvent _event = new ManualResetEvent(false); public void Run() { while (true) { DoSomethingThatTakesSeveralSeconds(); if (_event.WaitOne(timeout)) break; } } public void Stop() { _event.Set(); thread.Join(); } 

优雅地停止线程的方法是让它自己完成。 所以在worker方法中你可以有一个布尔变量来检查我们是否要中断。 默认情况下,它将设置为false ,当您从主线程将其设置为false时,它将通过断开处理循环来停止扫描操作。

我建议保持简单:

 while (m_shouldRun) { DoSomethingThatTakesSeveralSeconds(); for (int i = 0; i < 5; i++) // example: 5 seconds sleep { if (!m_shouldRun) break; Thread.Sleep(1000); } } public void Stop() { m_shouldRun = false; // maybe thread.Join(); } 

这具有以下优点:

  • 它闻起来像忙碌的等待,但事实并非如此。 $ NUMBER_OF_SECONDS检查在等待阶段完成,这与在真正忙碌等待中完成的数千次检查无法比较。
  • 它很简单,大大降低了multithreading代码中出错的风险。 你所有的Stop方法需要做的是将m_shouldRun设置为false并且(可能)调用Thread.Join (如果线程必须在Stop之前完成)。 不需要同步原语(除了将m_shouldRun标记为volatile)。

我想出了单独安排任务:

 using System; using System.Threading; namespace ProjectEuler { class Program { //const double cycleIntervalMilliseconds = 10 * 60 * 1000; const double cycleIntervalMilliseconds = 5 * 1000; static readonly System.Timers.Timer scanTimer = new System.Timers.Timer(cycleIntervalMilliseconds); static bool scanningEnabled = true; static readonly ManualResetEvent scanFinished = new ManualResetEvent(true); static void Main(string[] args) { scanTimer.Elapsed += new System.Timers.ElapsedEventHandler(scanTimer_Elapsed); scanTimer.Enabled = true; Console.ReadLine(); scanningEnabled = false; scanFinished.WaitOne(); } static void scanTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { scanFinished.Reset(); scanTimer.Enabled = false; if (scanningEnabled) { try { Console.WriteLine("Processing"); Thread.Sleep(5000); Console.WriteLine("Finished"); } finally { scanTimer.Enabled = scanningEnabled; scanFinished.Set(); } } } } }