积极答复者
为什么遍历 IEnumerable<Task<IEnumerable<TResult>>> 时引发大量 TaskCanceledException 异常?

问题
-
概述:我有个爬虫项目,当用户在文本框输入关键字时,返回爬取的结果集,结果集是分页的。你可以想象为我正在做一个本地的搜索引擎。
环境:WIN10 + VS2019 + C#/.NET4.7.2
问题:见代码及注释,问题很奇怪。
代码:
测试: private async void Button_Click(object sender, RoutedEventArgs e) { var args = new QueryArgs("xxx", "xxxx"); var svc = await RecordService.CreateAsync(args); var pages = svc.Query(); // 💥注意:如果我只 Skip(少量)、Task(少量),那么似乎一切正常, // 但如果我跳过太多元素,或者直接选择 Last(),那么下面的 await page 操作要等待很久, // 并且“输出”窗口提示出大量 “System.Threading.Tasks.TaskCanceledException” 异常, // 引发的异常数量似乎等于 Skip() 的数量
// ✨补充:如果我挂 VPN,则 Skip(100).Take(10),可以输出 2~10 页内容,第一页未获取到(奇怪)!
// 如果我挂 VPN 且 Skip(1000).Take(10), 则引发大量 “System.Net.Http.HttpRequestException” 异常。 var parts = pages.Skip(100).Take(2); foreach (var page in parts) { var p = await page; foreach (var r in p) { Debug.WriteLine(r); } } } 实现: // 查询返回一个包含页的集合,每个页中有多条记录。 public override IEnumerable<Task<IEnumerable<Record>>> Query(CancellationToken cancellationToken = default) { return Enumerable.Range(1, PageCount).Select(p => GetRecordsAsync(p, cancellationToken: cancellationToken)); } protected async Task<IEnumerable<Record>> GetRecordsAsync(int page, int startIndex = 1, CancellationToken cancellationToken = default) { string html = await JumpPageAsync(page, cancellationToken); var root = XpathHelper.GetRootNode(html); if (root == null) { return null; } var records = ParseAll(root, startIndex); return records; } public virtual async Task<string> JumpPageAsync(int page, CancellationToken cancellationToken = default) { // 通过修改 POST 的参数来实现跳页。 QueryArgs.JumpPage(page); string html = await GetHtmlAsync(cancellationToken); return html; } protected virtual async Task<string> GetHtmlAsync(CancellationToken cancellationToken = default) { var formData = await QueryArgs.ToFormDataAsync(); var resp = await _HttpClient.PostAsync(Url, new FormUrlEncodedContent(formData), cancellationToken); string html = await resp.Content.ReadAsStringAsync(); return html; }
END
- 已编辑 CodingNinja10 2019年5月28日 11:55
答案
全部回复
-
Hi CodingNinja10,
你的问题挺奇怪的, TaskCanceledException: 一般用于任务取消的异常。
所以我建议你检查pages获取的异步操作是否都已经将所有页获取了。
var pages = svc.Query();
请确保所有页获取之后,再尝试看看。
var parts = pages.Skip(100).Take(2);
Best regards
Yong LuMSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.- 已标记为答案 CodingNinja10 2019年6月4日 13:54
- 取消答案标记 CodingNinja10 2019年6月4日 13:54
-
Hi CodingNinja10,
这个原因暂时无法得知,我建议你可以多测试,然后通过结果分析。最好能定位到具体报错的直接代码
比如是否有网络环境的因素,
是否有访问网页的因素
Best regards
Yong LuMSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com. -
我想补充一点,根本原因在于
Query()
返回的结果是
IEnumerable<Task<string>>
这个 Task 包含了 Http 操作,Linq 操作直接 await 了全部元素,这使得即使我访问 IEnumerable<Task> 中最后一个 Task,Linq 也会枚举并 await 前面 N-1 个元素,这太慢了!
具体补充内容放大见图示:
如果可以,能帮我在 Stackoverflow 上提问此疑惑吗?我的英语不好,难以描述我的问题。
- 已编辑 CodingNinja10 2019年6月2日 14:19
-
我想补充一点,根本原因在于
Query()
返回的结果是
IEnumerable<Task<string>>
这个 Task 包含了 Http 操作,Linq 操作直接 await 了全部元素,这使得即使我访问 IEnumerable<Task> 中最后一个 Task,Linq 也会枚举并 await 前面 N-1 个元素,这太慢了!
具体补充内容放大见图示:
如果可以,能帮我在 Stackoverflow 上提问此疑惑吗?我的英语不好,难以描述我的问题。
Hi CodingNinja10,
>> 这个 Task 包含了 Http 操作,Linq 操作直接 await 了全部元素,这使得即使我访问 IEnumerable<Task> 中最后一个 Task,Linq 也会枚举并 await 前面 N-1 个元素,这太慢了!
主要还是查询时,获取所有数据时花费的时间比较长。
你可以访问ADO.NET 与 LINQ 论坛,或者ADO.NET Entity Framework and LINQ to Entities ,看看是否有好的建议。
>> 如果可以,能帮我在 Stackoverflow 上提问此疑惑吗?我的英语不好,难以描述我的问题。
这个可以慢慢的整理你的问题,通过一些辅助工具时可以正常提出问题的。
Best regards
Yong Lu
MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com. -