Thread.Interrupt 在应用程序关闭时停止长时间睡眠 - 是否有更好的方法
-
16-09-2019 - |
题
我有一个小的后台线程,它在应用程序的生命周期内运行 - 但是当应用程序关闭时,该线程应该正常退出。
问题是该线程以 15 分钟的间隔运行一些代码 - 这意味着它会休眠很多。
现在,为了让它脱离睡眠状态,我向它抛出一个中断 - 然而,我的问题是,是否有更好的方法,因为中断会生成 ThreadInterruptedException。
这是我的代码的要点(有点伪):
public class BackgroundUpdater : IDisposable
{
private Thread myThread;
private const int intervalTime = 900000; // 15 minutes
public void Dispose()
{
myThread.Interrupt();
}
public void Start()
{
myThread = new Thread(ThreadedWork);
myThread.IsBackground = true; // To ensure against app waiting for thread to exit
myThread.Priority = ThreadPriority.BelowNormal;
myThread.Start();
}
private void ThreadedWork()
{
try
{
while (true)
{
Thread.Sleep(900000); // 15 minutes
DoWork();
}
}
catch (ThreadInterruptedException)
{
}
}
}
解决方案
绝对有更好的方法 - 要么使用 Monitor.Wait
/脉冲 而不是睡眠/中断,或使用 Auto
/ManualResetEvent
. 。(你可能想要一个 ManualResetEvent
在这种情况下。)
就我个人而言,我是 Wait/Pulse 的粉丝,可能是因为它类似于 Java 的 wait()/notify() 机制。然而,有时候重置事件确实更有用。
你的代码看起来像这样:
private readonly object padlock = new object();
private volatile bool stopping = false;
public void Stop() // Could make this Dispose if you want
{
stopping = true;
lock (padlock)
{
Monitor.Pulse(padlock);
}
}
private void ThreadedWork()
{
while (!stopping)
{
DoWork();
lock (padlock)
{
Monitor.Wait(padlock, TimeSpan.FromMinutes(15));
}
}
}
更详细的可以看我的 线程教程, ,特别是以下页面 死锁、等待和脉冲, ,页面上 等待句柄. 。乔·阿尔巴哈里 还有教程 其中涵盖相同的主题并对它们进行比较。
我还没有详细查看,但如果并行扩展也有一些使这变得更容易的功能,我不会感到惊讶。
其他提示
您可以使用一个事件来检查过程应该结束这样的:
var eventX = new AutoResetEvent(false);
while (true)
{
if(eventX.WaitOne(900000, false))
{
break;
}
DoWork();
}
有在.NET 4 CancellationTokenSource
类和更高,这简化该任务有点。
private readonly CancellationTokenSource cancellationTokenSource =
new CancellationTokenSource();
private void Run()
{
while (!cancellationTokenSource.IsCancellationRequested)
{
DoWork();
cancellationTokenSource.Token.WaitHandle.WaitOne(
TimeSpan.FromMinutes(15));
}
}
public void Stop()
{
cancellationTokenSource.Cancel();
}
不要忘了CancellationTokenSource
是一次性的,所以一定要正确处理它。
的一种方法可能是添加一个取消事件或委托该线程将订阅。当取消事件是调用,线程可以停止本身。
我绝对喜欢乔恩长柄水杓答案。然而,这种的可能的容易有点明白了,也应该工作:
public class BackgroundTask : IDisposable
{
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Stop()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
}
public void Dispose()
{
this.cancellationTokenSource.Dispose();
}
private void ThreadedWork(object state)
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
}
或者,包括等待后台任务实际上已经停止(在这种情况下,处置必须由其他线程比后台线程上运行的一个调用,这当然是不完美的代码,它需要工人线程实际上已经开始):
using System;
using System.Threading;
public class BackgroundTask : IDisposable
{
private readonly ManualResetEventSlim threadedWorkEndSyncHandle;
private readonly CancellationTokenSource cancellationTokenSource;
private bool stop;
public BackgroundTask()
{
this.threadedWorkEndSyncHandle = new ManualResetEventSlim();
this.cancellationTokenSource = new CancellationTokenSource();
this.stop = false;
}
public void Dispose()
{
this.stop = true;
this.cancellationTokenSource.Cancel();
this.threadedWorkEndSyncHandle.Wait();
this.cancellationTokenSource.Dispose();
this.threadedWorkEndSyncHandle.Dispose();
}
private void ThreadedWork(object state)
{
try
{
using (var syncHandle = new ManualResetEventSlim())
{
while (!this.stop)
{
syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
if (!this.cancellationTokenSource.IsCancellationRequested)
{
// DoWork();
}
}
}
}
finally
{
this.threadedWorkEndSyncHandle.Set();
}
}
}
如果你看到过乔恩长柄水杓解决任何瑕疵和缺点,我想听到他们的声音,因为我总是乐于学习;-) 我想这是比较慢,并且使用更多的内存。因此应该不是在一个大的规模和短的时间内使用。任何其他