Qual è la differenza tra la restituzione di AsyncEnumerable con EnumeratorCancellation o il loop WithCancellation

Qual è la differenza tra la restituzione di AsyncEnumerable con EnumeratorCancellation o il loop WithCancellation

Sotto il cofano il token di cancellazione viene passato a GetAsyncEnumerator metodo comunque, secondo le fonti

namespace System.Collections.Generic
{
    public interface IAsyncEnumerable<out T>
    {
        IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
    }

    public interface IAsyncEnumerator<out T> : IAsyncDisposable
    {
        ValueTask<bool> MoveNextAsync();
        T Current { get; }
    }
}

Dovresti usare cancellationToken solo una volta, passando direttamente o utilizzando WithCancellation , questi metodi stanno facendo lo stesso. WithCancellation è il metodo di estensione per IAsyncEnumerable<T> , accettando un CancellationToken come argomento (usa lo stesso schema con ConfigureAwait ). In caso di [EnumeratorCancellation] il compilatore genera codice che passerà il token a GetAsyncEnumerator metodo

Il motivo di due modi diversi è descritto nella rivista MSDN


La cancellazione è cooperativa, quindi per poter annullare , avevi per implementare la cancellazione nel produttore codice GetLines , quello che fornisce il IAsyncEnumerable<Line> . Quindi, il produttore è un posto.

Ora, immagina che il metodo con cui il codice esegue qualcosa con quei dati si chiami ConsumeLines , diciamo che è un consumatore . Nel tuo caso potrebbe essere una base di codice, ma in generale potrebbe essere un'altra libreria, un altro repository, un'altra base di codice.

In quell'altra base di codice, non vi è alcuna garanzia che abbiano la stessa CancellationToken .

Quindi, come può un consumatore annullare?

Il consumatore deve superare un CancellationToken al IAsyncEnumerable<T>.GetAsyncEnumerator , ma non è esposto direttamente se stai utilizzando await foreach costruire.

Per risolvere questo problema, WithCancellation è stato aggiunto il metodo di estensione. Inoltra semplicemente il CancellationToken passato al IAsyncEnumerable sottostante avvolgendolo in un oggetto ConfiguredCancelableAsyncEnumerable.

A seconda di diverse condizioni, questo CancellationToken è collegato a quello nel produttore utilizzando CreateLinkedTokenSource in modo che il consumatore possa annullare utilizzando l'annullamento cooperativo implementato nel produttore in modo che non solo possiamo annullare il consumo, ma anche la produzione .

, dovresti agire in base al tuo CancellationToken utilizzando IsCancellationRequested o ThrowIfCancellationRequested nel tuo produttore codice. La cancellazione è cooperativa, se non la implementi in produttore , non potrai annullare la produzione i valori di IAsyncEnumerable .

Quanto a quando esattamente annullare - prima o dopo aver ceduto - dipende totalmente da te, l'idea è di evitare qualsiasi lavoro non necessario . In questo spirito, potresti anche verificare la cancellazione nella prima riga del tuo metodo, per evitare di inviare una richiesta http non necessaria.

Ricorda che cancellare il consumo dei valori, non è necessariamente la stessa cosa che annullare la produzione dei valori.

Anche in questo caso, produttore e consumatore potrebbero trovarsi in basi di codice diverse e potrebbero utilizzare CancellationTokens da un diverso CancellationTokenSources .

Per collegare quelli diversi CancellationTokens insieme è necessario utilizzare il EnumeratorCancellation attributo .

Si prega di leggere una spiegazione più approfondita nel mio articolo EnumeratorCancellation:il parametro CancellationToken dall'IAsyncEnumerable.GetAsyncEnumerator generato non verrà utilizzato