reced.intrupt لوقف النوم الطويل عند إيقاف تشغيل التطبيق - هل هناك نهج أفضل

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

  •  16-09-2019
  •  | 
  •  

سؤال

أواجه خيط خلفية صغير يمتد لتطبيقات العمر - ولكن عندما يكون التطبيق إيقاف التشغيل، يجب أن يخرج مؤشر الترابط بأمان.

المشكلة هي أن الموضوع يدير بعض التعليمات البرمجية في فاصل زمني 15 دقيقة - مما يعني أنه ينام كثيرا.

الآن من أجل إخراجها من النوم، أقيم مقاطعة في ذلك - سؤالي، ومع ذلك، إذا كان هناك نهج أفضل لهذا، لأن المقاطعات تولد ThreadInterruptException.

إليك GIST من التعليمات البرمجية (الزائفة إلى حد ما):

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 () / إعلام () آلية (). ومع ذلك، هناك بالتأكيد الأوقات التي تكون فيها أحداث إعادة التعيين أكثر فائدة.

سيبدو الكود الخاص بك شيئا مثل هذا:

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 يمكن التخلص منها، لذلك تأكد من التخلص منها بشكل صحيح.

قد تكون إحدى الطرق لإضافة حدث إلغاء أو تفويض أن الموضوع سوف يشترك في. عند استدعاء حدث إلغاء، يمكن أن يتوقف الخيط نفسه.

أنا تماما مثل إجابة جون خسائر. ومع ذلك، هذا ربما كن أسهل قليلا في فهم وينبغي أيضا العمل:

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 SKETS، أود أن أسمعها وأنا أستمتع دائما بالتعلم ؛-) أعتقد أن هذا أبطأ ويستخدم المزيد من الذاكرة ويجب عدم استخدامه في إطار كبير وقصير. اي شيء اخر؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top