Как распространить статус отмененной задачи на задачу продолжения - PullRequest
4 голосов
/ 26 января 2011

Я использую библиотеку параллельных задач в своем приложении. У меня есть задача (назовем это «DoSomething»), которая может быть отменена. Независимо от того, была ли задача выполнена успешно, отменена или успешно завершена, к этой задаче прикреплено продолжение, которое выполняет некоторую очистку.

В коде, который запускает эту задачу, я хочу вернуть объект задачи, состояние которого (сбой, отменено, выполнено до завершения) отражает состояние задачи DoSomething, однако важно, чтобы возвращаемая мной задача не отображала этот статус до задача продолжения выполняется.

Вот пример:

public Task Start(CancellationToken token)
{
    var doSomethingTask = Task.Factory.StartNew(DoSomething
                                                , token);

    var continuationTask = doSomethingTask.ContinueWith
                (
                 (antecedent) =>
                     {
                         if (antecedent.IsFaulted || antecedent.IsCanceled)
                         {
                             //Do failure-specific cleanup
                         }

   //Do general cleanup without regard to failure or success
                      }
                 );

//TODO: How do I return a Task obj which Status reflect the status of doSomethingTask,
//but will not transition to that status until continuationTask completes?
}

Я мог бы использовать TaskCompletionSource, но это кажется глупым. Любые другие идеи?

1 Ответ

7 голосов
/ 29 мая 2011

Я думаю, что TaskCompletionSource на самом деле идеально подходит для этого сценария. То есть вы пытаетесь вернуть Task как сигнал о завершении вашей работы, но вручную контролируете, когда и как эта задача сообщает о своем состоянии. Вы можете легко скрыть требуемую для этого плиту котла с помощью метода расширения, подобного этому:

public static Task<T> WithCleanup<T>(this Task<T> t, Action<Task<T>> cleanup) {
    var cleanupTask = t.ContinueWith(cleanup);
    var completion = new TaskCompletionSource<T>();
    cleanupTask.ContinueWith(_ => {
        if(t.IsCanceled) {
            completion.SetCanceled();
        } else if(t.IsFaulted) {
            completion.SetException(t.Exception);
        } else {
            completion.SetResult(t.Result);
        }
    });
    return completion.Task;
}

и назовите это так:

var doSomethingTask = Task.Factory
  .StartNew<object>(DoSomething, token)
  .WithCleanup(Cleanup);

Единственное настоящее предостережение в том, что вы не можете сделать это с простым старым Task, поскольку не существует неуниверсального TaskCompletionSource.

...