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

时间:2010-09-13 08:10:56

标签: c# multithreading

问题陈述

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

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

尝试解决方案

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

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

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

现在我需要制作我的Stop功能。所以我开始:

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);
        }
    }
}

问题

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

4 个答案:

答案 0 :(得分:9)

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

答案 1 :(得分:8)

另一种选择是使用事件:

private ManualResetEvent _event = new ManualResetEvent(false);


public void Run() 
{
 while (true)
 {
    DoSomethingThatTakesSeveralSeconds();
    if (_event.WaitOne(timeout))
      break;
 }
}

public void Stop() 
{
   _event.Set();
   thread.Join();
}

答案 2 :(得分:1)

我建议保持简单:

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次检查,这与在真正忙碌的等待中完成的数千次检查无法比较。
  • 这很简单,大大降低了多线程代码中出错的风险。您的Stop方法需要做的就是将m_shouldRun设置为false,然后(可能)调用Thread.Join(如果线程必须在Stop之前完成) 。不需要同步原语(将m_shouldRun标记为易失性除外)。

答案 3 :(得分:0)

我想出了单独安排任务:

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();
                }
            }
        }
    }
}