Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancelling tasks in the Dispose method

I have a class which spawns various tasks which can run indefinitely. When this object is disposed, I want to stop those tasks from running.

Is this the correct approach:

public class MyClass : IDisposable
{
    // Stuff

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            queueCancellationTokenSource.Cancel();
            feedCancellationTokenSource.Cancel();
        }
    }
}
like image 865
Dave New Avatar asked Oct 05 '13 10:10

Dave New


1 Answers

You're on the right track. However, I would suggest waiting for the task to terminate before returning from the Dispose method, in order to avoid race conditions where the task continues to operate after the object has been disposed. Also dispose the CancellationTokenSource.

Update: If you're on .NET Core 3.0 or later, you should make your class implement IAsyncDisposable and await your task from the DisposeAsyncCore method. I've updated the example below to reflect this.

using System;
using System.Threading;
using System.Threading.Tasks;

public class MyClass : IDisposable, IAsyncDisposable
{
    private readonly CancellationTokenSource feedCancellationTokenSource =
        new CancellationTokenSource();
    private readonly Task feedTask;

    public MyClass()
    {
        feedTask = Task.Factory.StartNew(() =>
        {
            while (!feedCancellationTokenSource.IsCancellationRequested)
            {
                // do finite work
            }
        });
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            feedCancellationTokenSource.Cancel();
            feedTask.Wait();

            feedCancellationTokenSource.Dispose();
            feedTask.Dispose();
        }
    }

    public async ValueTask DisposeAsync()
    {
        await DisposeAsyncCore().ConfigureAwait(false);
        Dispose(false);
        GC.SuppressFinalize(this);
    }

    protected virtual async ValueTask DisposeAsyncCore()
    {
        feedCancellationTokenSource.Cancel();
        await feedTask.ConfigureAwait(false);

        feedCancellationTokenSource.Dispose();
        feedTask.Dispose();
    }
}

// Sample usage:
public static class Program
{
    public static async Task Main()
    {
        await using (new MyClass())
        {
            // do something else
        }
        
        Console.WriteLine("Done");
    }
}
like image 89
Douglas Avatar answered Oct 19 '22 05:10

Douglas