Thread.Interrupt 在应用程序关闭时停止长时间睡眠 - 是否有更好的方法

StackOverflow https://stackoverflow.com/questions/1141617

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

如果你看到过乔恩长柄水杓解决任何瑕疵和缺点,我想听到他们的声音,因为我总是乐于学习;-) 我想这是比较慢,并且使用更多的内存。因此应该不是在一个大的规模和短的时间内使用。任何其他

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top