6

我正在将一些 async/await 代码转换为链式任务,因此我可以在已发布的框架中使用它。等待代码看起来像这样

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

4

3 回答 3

4

是的,该框架带有一个方便的 Unwrap() 扩展方法,正是您想要的。

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

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

于 2012-04-25T13:20:05.200 回答
1

我认为这几乎是实现你想要的唯一方法。延续 API 不支持将不同的任务链接在一起,因此您必须求助于TaskCompletionSource您必须协调工作的方式。

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

于 2011-07-30T17:10:45.770 回答
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;
}
于 2011-09-04T04:09:25.240 回答