没有TaskCompletionSource的任务链接?

时间:2011-07-30 16:29:19

标签: c# task-parallel-library async-ctp

我正在将一些异步/等待代码转换为链式任务,因此我可以在已发布的框架中使用它。等待代码如下所示

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() {}

我希望链接InvokeMemorize以返回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 个答案:

答案 0 :(得分:4)

是的,该框架附带了一个方便的Unwrap()扩展方法,可以满足您的需求。

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

如果您正在取消,那么您显然需要将取消令牌传递到适当的位置。

答案 1 :(得分:1)

我认为这几乎是实现目标的唯一途径。连续API不支持将不同的任务链接在一起,因此您必须使用TaskCompletionSource来协调工作。

我没有在这台机器上安装Async CTP,但为什么不用反编译器(或ILDASM,如果你知道如何读取IL)查看代码,看看它在做什么。我敢打赌,它与你的TCS代码非常相似。

答案 2 :(得分:0)

您可以使用附加的子任务。只有当所有子任务完成后,父任务才会转换为已完成状态。异常将传播到父任务。 您将需要一个结果持有者,因为结果将在父任务的委托完成后分配,但将在父任务继续运行时设置。

像这样:

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;
}