Recurring Background Task [closed]

2019-03-02 08:50发布

问题:

I am just starting to try to use "Tasks" instead of Threads and am trying to implement an object with a background "cleanup" task that runs every 5 minutes as long as the object is in use but which should not block garbage collection.

Something crudely along the lines of (which obviously doesn't work...)

public class Foo : IDisposable
{
    private CancellationTokenSource _tokenSource = new CancellationTokenSource();

    public Foo()
    {
        Cleanup();      
    }

    public void Dispose()
    {
        _tokenSource.Cancel();
    }

    public void Cleanup()
    {
        Task.Delay(TimeSpan.FromSeconds(5), _tokenSource.Token).ContinueWith(t => 
        {
            //Do Work
            if (!_tokenSource.IsCancellationRequested)
            {
                Cleanup();
            }
        }, TaskContinuationOptions.NotOnCanceled);
    }
}

What is the correct way to implement?

EDIT In response to I3arnon's question, I am using IDisposable because when I am done with the object, I want it to be garbage collected. For example, without the f.Dispose() below (which cancels the task, f does not appear to be Garbage Collected, or the cleanup task canceled. Is there a better way to implement?

var f = new Foo();
var r = new WeakReference(f);
Thread.Sleep(TimeSpan.FromSeconds(15));
f.Dispose();
f = null;
System.GC.Collect();
Thread.Sleep(TimeSpan.FromSeconds(5));
Console.WriteLine(r.IsAlive);

回答1:

Is that good enough? You can also make this class generic and reusable by accepting a delegate to run inside the while. I'm not really sure why you need it to be an IDisposable though...

public class Foo : IDisposable
{
    private CancellationTokenSource _tokenSource = new CancellationTokenSource();

    public Foo()
    {
        Task.Run(async () => await CleanupAsync());
    }

    public void Dispose()
    {
        _tokenSource.Cancel();
    }

    public async Task CleanupAsync()
    {
        while (!_tokenSource.Token.IsCancellationRequested)
        {
           // Do whatever cleanup you need to.

           await Task.Delay(TimeSpan.FromSeconds(5),_tokenSource.Token);
        }
    }
}

The async and await inside the Task.Run can be removed. They are just there for clarity. Task.Run(() => CleanupAsync());