Thread. 앱 셧다운시 장기 수면을 중지하려면 - 더 나은 접근 방식이 있습니까?

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

  •  16-09-2019
  •  | 
  •  

문제

응용 프로그램 수명을 위해 실행되는 작은 배경 스레드가 있습니다. 그러나 응용 프로그램이 종료되면 스레드가 우아하게 종료되어야합니다.

문제는 스레드가 15 분 간격으로 일부 코드를 실행한다는 것입니다. 이는 많이 자고 있음을 의미합니다.

이제 잠을 자지 않기 위해 인터럽트를 던지기 위해서는 인터럽트가 스레드 인터럽트를 생성하기 때문에 더 나은 접근 방식이 있다면 내 질문입니다.

다음은 내 코드의 요점입니다 (다소 의사) :

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 이 경우.)

개인적으로 나는 대기/펄스 팬 일 것입니다. 아마도 Java 's 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();
}

거기 있습니다 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 일회용이므로 제대로 처리하십시오.

한 가지 방법은 취소 이벤트를 추가하거나 스레드가 구독 할 위임입니다. 취소 이벤트가 호출되면 스레드가 스스로 중지 될 수 있습니다.

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

또는 배경 작업이 실제로 중지되기를 기다리는 것을 포함하여 (이 경우, 배경 스레드가 실행되는 것보다 다른 스레드에 의해 폐기되어야하며, 물론 이것은 완벽한 코드가 아니므로 작업자 스레드가 실제로 필요합니다. 시작했다):

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 솔루션에 대한 결함과 단점이 있으면 항상 학습을 즐기기 때문에 듣고 싶습니다. ;-) 나는 이것이 더 느리고 더 많은 메모리를 사용하여 대규모 및 짧은 기간으로 사용해서는 안된다고 생각합니다. 다른 사람?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top