Un patrón para la auto-cancelación y reinicio de tareas.

¿Existe un patrón establecido recomendado para la cancelación automática y el reinicio de tareas?

Por ejemplo, estoy trabajando en la API para el corrector ortográfico de fondo. La sesión de corrección ortográfica se envuelve comoTask. Cada nueva sesión debe cancelar la anterior y esperar su finalización (para reutilizar adecuadamente los recursos como el proveedor de servicios de corrección ortográfica, etc.).

Se me ha ocurrido algo como esto:

class Spellchecker
{
    Task pendingTask = null; // pending session
    CancellationTokenSource cts = null; // CTS for pending session

    // SpellcheckAsync is called by the client app
    public async Task<bool> SpellcheckAsync(CancellationToken token)
    {
        // SpellcheckAsync can be re-entered
        var previousCts = this.cts;
        var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
        this.cts = newCts;

        if (IsPendingSession())
        {
            // cancel the previous session and wait for its termination
            if (!previousCts.IsCancellationRequested)
                previousCts.Cancel();
            // this is not expected to throw
            // as the task is wrapped with ContinueWith
            await this.pendingTask; 
        }

        newCts.Token.ThrowIfCancellationRequested();
        var newTask = SpellcheckAsyncHelper(newCts.Token);

        this.pendingTask = newTask.ContinueWith((t) => {
            this.pendingTask = null;
            // we don't need to know the result here, just log the status
            Debug.Print(((object)t.Exception ?? (object)t.Status).ToString());
        }, TaskContinuationOptions.ExecuteSynchronously);

        return await newTask;
    }

    // the actual task logic
    async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
    {
        // do not start a new session if the the previous one still pending
        if (IsPendingSession())
            throw new ApplicationException("Cancel the previous session first.");

        // do the work (pretty much IO-bound)
        try
        {
            bool doMore = true;
            while (doMore)
            {
                token.ThrowIfCancellationRequested();
                await Task.Delay(500); // placeholder to call the provider
            }
            return doMore;
        }
        finally
        {
            // clean-up the resources
        }
    }

    public bool IsPendingSession()
    {
        return this.pendingTask != null &&
            !this.pendingTask.IsCompleted &&
            !this.pendingTask.IsCanceled &&
            !this.pendingTask.IsFaulted;
    }
}

La aplicación cliente (la interfaz de usuario) debería poder llamarSpellcheckAsync tantas veces como desee, sin preocuparse por cancelar una sesión pendiente. El principaldoMore el bucle se ejecuta en el subproceso de la interfaz de usuario (ya que involucra a la interfaz de usuario, mientras que todas las llamadas del proveedor del servicio de corrección ortográfica están vinculadas a IO).

Me siento un poco incómodo por el hecho de que tuve que dividir la API en dos partes,SpellcheckAsync ySpellcheckAsyncHelper, pero no puedo pensar en una mejor manera de hacer esto, y aún no se ha probado.

Respuestas a la pregunta(4)

Su respuesta a la pregunta