Цепочка задач без TaskCompletionSource? - PullRequest
5 голосов
/ 30 июля 2011

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

public async Task<TraumMessage> Get() {
  var message = await Invoke("GET");
  var memorized = await message.Memorize();
  return memorized;
}

, где

Task<TraumMessage> Invoke(string verb) {}
Task<TraumMessage> Memorize() {}

Я надеялся соединить Invoke и Memorize, чтобы вернуть задачу, созданную Memorize, но в результате Task<Task<TraumMessage>. Решение, которое я нашел, - это TaskCompletionSource<TraumMessage> как мой сигнал:

public Task<TraumMessage> Get() {
  var completion = new TaskCompletionSource<TraumMessage>();
  Invoke("GET").ContinueWith( t1 => {
     if(t1.IsFaulted) {
       completion.SetException(t1.Exception);
       return;
     }
     t1.Result.Memorize().ContinueWith( t2 => {
       if(t2.IsFaulted) {
         completion.SetException(t2.Exception);
         return;
       }
       completion.SetResult(t2.Result);
     });
  });
  return completion.Task;
}

Есть ли способ сделать это без TaskCompletionSource?

Ответы [ 3 ]

4 голосов
/ 25 апреля 2012

Да, фреймворк поставляется с удобным методом расширения Unwrap () для именно того, что вы хотите.

Invoke("GET").ContinueWith( t => t.Result.Memorize() ).Unwrap();

Если вы делаете отмену, вам, очевидно, нужно будет передать токены отмены в соответствующие места.

1 голос
/ 30 июля 2011

Я думаю, что это практически единственный способ достичь того, чего вы хотите. Объединение разрозненных задач не поддерживается API-интерфейсами продолжения, поэтому вам придется прибегнуть к использованию TaskCompletionSource, как вы должны координировать работу.

У меня не установлен Async CTP на этом компьютере, но почему бы вам не взглянуть на код с помощью декомпилятора (или ILDASM, если вы знаете, как читать IL), чтобы увидеть, что он делает. Бьюсь об заклад, он делает что-то очень похожее на ваш код TCS под обложками.

0 голосов
/ 04 сентября 2011

Вы можете использовать вложенные дочерние задачи. Родительская задача перейдет в завершенное состояние только после завершения всех дочерних задач. Исключения распространяются на родительскую задачу. Вам понадобится держатель результата, так как результат будет назначен после , когда делегат родительской задачи завершился, но будет установлен при выполнении продолжения родительской задачи.

Как это:

public class Holder<T> where T: class
{
   public T Value { get; set; }
}

public Task<Holder<TraumMessage>> Get() {
  var invokeTask = Invoke("GET");
  var result = invokeTask.ContinueWith<Holder<TraumMessage>>(t1 => {
    var holder = new Holder<TraumMessage>();
    var memorizeTask = t1.Result.Memorize();
    memorizeTask.ContinueWith(t2 => {
      holder.Value = t2.Result;
    }, TaskContinuationOptions.AttachedToParent);
    return holder;
  });
  return result;
}
...