5

アプリケーションの存続期間中実行される小さなバックグラウンド スレッドがありますが、アプリケーションがシャットダウンされると、スレッドは正常に終了するはずです。

問題は、スレッドが 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)
        {
        }
    }
}
4

5 に答える 5

14

絶対に良い方法があります - Sleep/Interrupt の代わりにMonitor.Wait/ PulseAutoを使うか、 /を使いますManualResetEvent。(この場合、おそらく aManualResetEventが必要です。)

個人的に私は 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));
        }
    }
}

詳細については、私のスレッド チュートリアル、特にデッドロック、待機、パルスに関するページ、待機ハンドルに関するページを参照してください。Joe Albahariは、同じトピックを扱ってそれらを比較するチュートリアルも持っています。

まだ詳しく調べていませんが、Parallel Extensions にこれを簡単にする機能があったとしても驚かないでしょう。

于 2009-07-17T06:14:05.947 に答える
2

イベントを使用して、プロセスが次のように終了するかどうかを確認できます。

var eventX = new AutoResetEvent(false);
while (true)
{
    if(eventX.WaitOne(900000, false))
    {
        break;
    }
    DoWork();
}
于 2009-07-17T06:27:58.727 に答える
1

CancellationTokenSource.NET 4 以降には、このタスクを少し簡素化するクラスがあります。

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使い捨てですので、適切に廃棄してください。

于 2013-10-14T03:49:51.153 に答える
0

Jon Skeetsの回答が絶対に好きです。ただし、これ少し理解しやすく、動作するはずです。

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

または、バックグラウンド タスクが実際に停止するのを待つことも含めて (この場合、Dispose はバックグラウンド スレッドが実行されているスレッド以外のスレッドによって呼び出される必要があります。もちろん、これは完全なコードではありません。ワーカー スレッドが実際に停止する必要があります)。開始しています):

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

Jon Skeets のソリューションに欠陥や欠点が見られる場合は、私はいつも学習を楽しんでいるので、それらを聞きたいです;-) これは遅く、より多くのメモリを使用するため、大規模で短い時間枠では使用しないでください. 他の?

于 2013-03-07T10:16:30.450 に答える
0

1 つの方法は、スレッドがサブスクライブするキャンセル イベントまたはデリゲートを追加することです。キャンセル イベントが呼び出されると、スレッドはそれ自体を停止できます。

于 2009-07-17T06:14:13.390 に答える