Question

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();
        }
    }
}
Was it helpful?

Solution

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");
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top