Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

70
Views
Should DisposeAsync throw background task exceptions, or leave it to the client to observe explicitly?

I don't think this question is a duplicate of "Proper way to deal with exceptions in DisposeAsync".

Let's say my class that implements IAsynsDisposable because it has a long-running background task, and DisposeAsync terminates that task. A familiar pattern might be the Completion property, e.g. ChannelReader<T>.Completion (despite ChannelReader doesn't implement IAsynsDisposable).

Is it considered a good practice to propagate the Completion task's exceptions outside DisposeAsync?

Here is a complete example that can be copied/pasted into a dotnet new console project. Note await this.Completion inside DisposeAsync:

try
{
    await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
    await Task.Delay(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
    Console.WriteLine(ex);
    Console.ReadLine();
}

class BackgroundService: IAsyncDisposable
{
    public Task Completion { get; }

    private CancellationTokenSource _diposalCts = new();

    public BackgroundService(TimeSpan timeSpan)
    {
        this.Completion = Run(timeSpan);
    }

    public async ValueTask DisposeAsync()
    {
        _diposalCts.Cancel();
        try
        {
            await this.Completion;
        }
        finally
        {
            _diposalCts.Dispose();
        }
    }

    private async Task Run(TimeSpan timeSpan)
    {
        try
        {
            await Task.Delay(timeSpan, _diposalCts.Token);
            throw new InvalidOperationException("Boo!");
        }
        catch (OperationCanceledException)
        {
        }
    }
}

Alternatively, I can observe service.Completion explicitly in the client code (and ignore its exceptions inside DiposeAsync to avoid them being potentially thrown twice), like below:

try
{
    await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
    await Task.Delay(TimeSpan.FromSeconds(3));
    await service.Completion;
}
catch (Exception ex)
{
    Console.WriteLine(ex);
    Console.ReadLine();
}

class BackgroundService: IAsyncDisposable
{
    public Task Completion { get; }

    private CancellationTokenSource _diposalCts = new();

    public BackgroundService(TimeSpan timeSpan)
    {
        this.Completion = Run(timeSpan);
    }

    public async ValueTask DisposeAsync()
    {
        _diposalCts.Cancel();
        try
        {
            await this.Completion;
        }
        catch
        {
            // the client should observe this.Completion
        }
        finally
        {
            _diposalCts.Dispose();
        }
    }

    private async Task Run(TimeSpan timeSpan)
    {
        try
        {
            await Task.Delay(timeSpan, _diposalCts.Token);
            throw new InvalidOperationException("Boo!");
        }
        catch (OperationCanceledException)
        {
        }
    }
}

Is there a concensus about which option is better?

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

For now, I've settled on a reusable helper class LongRunningAsyncDisposable (here's a gist, warning: barely tested yet), which allows:

  • to start a background task;
  • stop this task (via a cancellation token) by calling IAsyncDisposable.DisposeAsync at any time, in a thread-safe, concurrency-friendly way;
  • configure whether DisposeAsync should re-throw the task's exceptions (DisposeAsync will await the task's completion either way, before doing a cleanup);
  • observe the task's status, result and exceptions at any time via LongRunningAsyncDisposable.Completion property.
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!