怎么做 Task.ContinueWith<Task>(...)
How to do Task.ContinueWith<Task>(...)
我使用 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
我该如何完成?
更新
是的,我确实需要使用任务而不是 async/await,我真的知道我想要什么。
更新2
我修正了我的观点,现在我认为我选择的技术是错误的。如果有人怀疑,这里有一个 great example 好的代码。
这些天你应该避免 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)
}
but one of pool's threads will be blocked
情况并非如此,因为 ContinueWith
委托只会在 "antecedent" 任务完成后调用。 Result
不会阻塞,甚至不需要同步。
第二个 .Result
(ReadAsStringAsync().Result
) 正在阻塞。所以你必须把它变成另一个 ContinueWith
.
一般来说,一串连续的IOs会变成一串ContinueWith
。
一般来说,await
在这里更可取,但您表示这不适合您。
And yes, I do need to use Tasks instead of async/await and I really know what I want.
强烈推荐。我想不出不使用 async
/await
的充分理由,除非你坚持使用 .NET 4.0(即 Windows XP)。事实并非如此,因为您使用的是 HttpClient
和 Task.Run
。所以请记住,这个答案纯粹是指导性的,不推荐用于生产。
ContinueWith
调用可以是 "chained",有点类似于 Promise.then
在 JavaScript 中的工作方式,但开箱即用的 C# 链接语义比 JavaScript 更尴尬。
一方面,Task<Task<T>>
类型不会自动展开。有一个 Unwrap
方法可用。另一方面,使用 .Result
- 更自然地与 ContinueWith
一起使用的 TPL 遗留物 - 会将异常包装在 AggregateException
中,这可能会导致一种有趣的 "cascade" ,你的内部异常可以深埋在嵌套的 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
来封装其中的一些逻辑。哦,一定要在评论中道歉;即使未来的维护者是您自己,对这样的代码也只是礼貌的做法。 ;)
我使用 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我该如何完成?
更新
是的,我确实需要使用任务而不是 async/await,我真的知道我想要什么。
更新2
我修正了我的观点,现在我认为我选择的技术是错误的。如果有人怀疑,这里有一个 great example 好的代码。
这些天你应该避免 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)
}
but one of pool's threads will be blocked
情况并非如此,因为 ContinueWith
委托只会在 "antecedent" 任务完成后调用。 Result
不会阻塞,甚至不需要同步。
第二个 .Result
(ReadAsStringAsync().Result
) 正在阻塞。所以你必须把它变成另一个 ContinueWith
.
一般来说,一串连续的IOs会变成一串ContinueWith
。
一般来说,await
在这里更可取,但您表示这不适合您。
And yes, I do need to use Tasks instead of async/await and I really know what I want.
强烈推荐async
/await
的充分理由,除非你坚持使用 .NET 4.0(即 Windows XP)。事实并非如此,因为您使用的是 HttpClient
和 Task.Run
。所以请记住,这个答案纯粹是指导性的,不推荐用于生产。
ContinueWith
调用可以是 "chained",有点类似于 Promise.then
在 JavaScript 中的工作方式,但开箱即用的 C# 链接语义比 JavaScript 更尴尬。
一方面,Task<Task<T>>
类型不会自动展开。有一个 Unwrap
方法可用。另一方面,使用 .Result
- 更自然地与 ContinueWith
一起使用的 TPL 遗留物 - 会将异常包装在 AggregateException
中,这可能会导致一种有趣的 "cascade" ,你的内部异常可以深埋在嵌套的 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
来封装其中的一些逻辑。哦,一定要在评论中道歉;即使未来的维护者是您自己,对这样的代码也只是礼貌的做法。 ;)