アプリのシャットダウン時に長いスリープを停止するための 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));
        }
    }
}

詳細については、私の スレッド化のチュートリアル, 、特に次のページ デッドロック、待機およびパルス, のページ 待機ハンドル. 。ジョー・アルバハリ チュートリアルもあります 同じトピックを取り上げて比較します。

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

他のヒント

あなたは、プロセスは次のように終了すべきかどうかをチェックするには、イベントを使用することができます:

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が使い捨てであることを忘れて、あなたはそれを正しく処分してくださいしないでください。

一つの方法は、cancelイベントを追加したり、スレッドが購読することを委任するかもしれません。キャンセルイベントが起動されたとき、スレッド自体を停止することができます。

私は絶対ジョン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();
                }
            }
        }
    }
}
それとも、この場合には(実際に停止しているために、バックグラウンドタスクを待っ含め、処分がバックグラウンドスレッドが実行されているものよりも、他のスレッドによって呼び出されなければならない、そしてもちろん、これは完璧なコードではありません、それは労働者を必要とし実際に始めたのは、スレッド):

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();
        }
    }
}
あなたはジョンSkeetsソリューションの上に任意の欠陥や欠点が表示されている場合は、

は、私は常に学ん;-)楽しみとしてそれらを聞きたいのですが 私は、これは遅く、より多くのメモリを使用するため、大規模かつ短期間で使用すべきではないと思います。その他?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top