问题陈述
我有一个工作线程,它基本上扫描一个文件夹,进入其中的文件,然后睡了一会儿。扫描操作可能需要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);
}
}
}
问题
首选方法是什么?有第三种方法更可取吗?
答案 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();
}
这具有以下优点:
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();
}
}
}
}
}