admin管理员组

文章数量:1123865

I have created a Background Service with the return of an infinite asynchronous operation. At a random moment, IsCancellationRequested = true is applied. Basically, as I understand it, this happens in idle time. What can cause this behavior and what to do about it?

internal sealed class EventExecutorService : BackgroundService
{
    public EventExecutorService()
    {
    }

    protected override Task ExecuteAsync(CancellationToken cancellationToken)
    {
        return new PoolInfiniteTasksManager(3).Run(cancellationToken);
    }       
}

I have created a Background Service with the return of an infinite asynchronous operation. At a random moment, IsCancellationRequested = true is applied. Basically, as I understand it, this happens in idle time. What can cause this behavior and what to do about it?

internal sealed class EventExecutorService : BackgroundService
{
    public EventExecutorService()
    {
    }

    protected override Task ExecuteAsync(CancellationToken cancellationToken)
    {
        return new PoolInfiniteTasksManager(3).Run(cancellationToken);
    }       
}
Share Improve this question asked yesterday Web FRWeb FR 515 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Take a look at the current BackgroundService definition:

public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task? _executeTask;
    private CancellationTokenSource? _stoppingCts;

    /// <summary>
    /// Gets the Task that executes the background operation.
    /// </summary>
    /// <remarks>
    /// Will return <see langword="null"/> if the background operation hasn't started.
    /// </remarks>
    public virtual Task? ExecuteTask => _executeTask;

    /// <summary>
    /// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task that represents
    /// the lifetime of the long running operation(s) being performed.
    /// </summary>
    /// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
    /// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
    /// <remarks>See <see href="https://learn.microsoft.com/dotnet/core/extensions/workers">Worker Services in .NET</see> for implementation guidelines.</remarks>
    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    /// <summary>
    /// Triggered when the application host is ready to start the service.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
    /// <returns>A <see cref="Task"/> that represents the asynchronous Start operation.</returns>
    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Create linked token to allow cancelling executing task from provided token
        _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

        // Store the task we're executing
        _executeTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it, this will bubble cancellation and failure to the caller
        if (_executeTask.IsCompleted)
        {
            return _executeTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    /// <summary>
    /// Triggered when the application host is performing a graceful shutdown.
    /// </summary>
    /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
    /// <returns>A <see cref="Task"/> that represents the asynchronous Stop operation.</returns>
    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executeTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts!.Cancel();
        }
        finally
        {
#if NET8_0_OR_GREATER
            await _executeTask.WaitAsync(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
#else
            // Wait until the task completes or the stop token triggers
            var tcs = new TaskCompletionSource<object>();
            using CancellationTokenRegistration registration = cancellationToken.Register(s => ((TaskCompletionSource<object>)s!).SetCanceled(), tcs);
            // Do not await the _executeTask because cancelling it will throw an OperationCanceledException which we are explicitly ignoring
            await Task.WhenAny(_executeTask, tcs.Task).ConfigureAwait(false);
#endif
        }

    }

    /// <inheritdoc />
    public virtual void Dispose()
    {
        _stoppingCts?.Cancel();
    }
}

So, there are only three reasons to get cancellation:

  1. StopAsync method has been called by .NET Generic Host.
  2. BackgroundService instance has been disposed.
  3. The start process has been aborted.

本文标签: cCancellationToken in BackgroundService is triggered unexpectedlyStack Overflow