Связывание двух токенов отмены без избыточного аннулирования токенов - PullRequest
1 голос
/ 04 мая 2020

Внутри метода, который получает CancellationToken (StartAsync), я хотел бы добавить внутренний CancellationToken, чтобы асинхронная операция могла быть отменена вызывающей стороной внешне или внутренне (например, путем вызова метода AbortAsync() ).

AFAIK, способ сделать это - использовать CreateLinkedCancellationTokenSource. Но его API кажется довольно неудобным, потому что мне нужно создать для этого два дополнительных CancellationTokenSource экземпляра и поскольку они реализуют IDisposable, я также не должен забывать их утилизировать. В результате мне нужно сохранить их обоих как участников для последующего удаления.

Я что-то упустил? Я считаю, что должен быть более простой способ присоединить дополнительный механизм отмены к существующему токену, который не заставляет меня поддерживать два CancellationTokenSource экземпляра.

public Task StartAsync(CancellationToken externalToken)
{
    this.actualCancellation = new CancellationTokenSource();
    this.linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(
        actualCancellation.Token, externalToken);
    this.execution = this.ExecuteAsync(this.linkedCancellation.Token);
    return this.execution;
}

public async Task AbortAsync()
{
    try
    {
        this.actualCancellation.Cancel();
        await this.execution;
    }
    catch
    {
    }
    finally
    {
        this.actualCancellation.Dispose();
        this.linkedCancellation.Dispose();
    }
}

1 Ответ

2 голосов
/ 04 мая 2020

Связанный источник отмены не является особым видом отмены. Это обычный источник отмены, который также «связан» с существующим токеном, т. Е. Источник будет отменен при отмене существующего токена. В остальном это обычный источник отмены, поэтому вы можете отменить его самостоятельно, как и любой другой источник отмены.

Таким образом, вам нужен только один источник отмены - тот, который связан с существующим токеном и также можно отменить вручную:

public Task StartAsync(CancellationToken externalToken)
{
    this.linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
    this.execution = this.ExecuteAsync(this.linkedCancellation.Token);
    return this.execution;
}

public async Task AbortAsync()
{
    try
    {
        this.linkedCancellation.Cancel();
        await this.execution;
    }
    catch
    {
    }
    finally
    {
        this.linkedCancellation.Dipose();
    }
}

В качестве дополнительного примечания я бы тщательно рассмотрел вопросы, связанные с пожизненным сроком службы, для такого рода разработки API. В настоящее время StartAsync выполняет распределение ресурсов, а AbortAsync выполняет очистку; Я бы порекомендовал проект, в котором они обрабатываются конструктором и Dispose (RAII).

...