如何做Task.ContinueWith <task>(...)

时间:2019-02-08 08:27:32

标签: .net task-parallel-library

我使用HttpClient,我想写这样的东西:

HttpClient client = ...;
Task<string> getContent()
{
    return client.GetAsync(...)
   .ContinueWith( t => t.Result.Content.ReadAsStringAsync() );
}

我知道我会写

.ContinueWith( t => t.Result.Content.ReadAsStringAsync().Result);

,但是池的线程之一将被阻止。 我想要像Google Task library中的continueWithTask。

我该怎么做?

更新

是的,我确实需要使用Tasks而不是async / await,我真的知道我想要什么。

UPDATE2

我修改了看法,现在我认为选择技术是错误的。如果有人怀疑,这里是great example的好代码。

3 个答案:

答案 0 :(得分:6)

这几天,除非有非常具体的原因,否则应避免使用ContinueWith / async await ;我怀疑这会起作用:

async Task<string> getContent()
{
    var foo = await client.GetAsync(...); // possibly with .ConfigureAwait(false)
    return await foo.Content.ReadAsStringAsync(); // possibly with .ConfigureAwait(false)
}

答案 1 :(得分:3)

  

是的,我确实需要使用Tasks而不是async / await,我真的知道我想要什么。

我强烈推荐Marc's answer。除非您坚持使用.NET 4.0(即Windows XP),否则我不认为不使用async / await的充分理由。事实并非如此,因为您正在使用HttpClientTask.Run。因此,请记住,此答案是纯粹是说明性的,并且不建议用于生产环境

ContinueWith调用可以被“链接”,类似于Promise.then在JavaScript中的工作方式,但是现成的C#链接语义比JavaScript笨拙。

一方面,Task<Task<T>>类型不会自动解包。有Unwrap方法可用。另外,使用.Result(更自然地与ContinueWith一起使用的TPL遗物)会将异常包装在AggregateException中,这可能会引起一种有趣的“级联”,其中您的内部异常可以被深埋在嵌套的AggregateException实例中。因此,AggregateException.Flatten的存在可以弄清事后的混乱状况。哦,你应该always pass a TaskScheduler to ContinueWith

这是第一次尝试,显式指定TaskScheduler,使用Unwrap来解开嵌套任务,并通过使用GetAwaiter().GetResult()而不是Result来避免嵌套异常:

Task<string> getContent()
{
  // I am so sorry, future coder, but I cannot use await here because <insert good reason>
  return Task.Run(()=> client.GetAsync(...))
      .ContinueWith(t => t.GetAwaiter().GetResult().Content.ReadAsStringAsync(), TaskScheduler.Default).Unwrap()
      .ContinueWith(t => t.GetAwaiter().GetResult(), TaskScheduler.Default);
}

如果您在代码中经常执行此操作,则可能要使用something like .Then来封装其中的某些逻辑。哦,一定要在评论中道歉。即使将来的维护者是你自己,这也是像这样的代码有礼貌的事情。 ;)

答案 2 :(得分:0)

  

但是池的线程之一将被阻塞

情况并非如此,因为仅在“先行”任务完成后才调用ContinueWith委托。 Result不会阻止,甚至没有同步。

第二个.ResultReadAsStringAsync().Result)被阻止了。因此,您必须将其转换为另一个ContinueWith

通常,顺序的IO序列变成ContinueWith的序列。

通常,这里最好使用await,但您表示这对您不起作用。