c#异常后继续
Continue after an exception in c#
我正在尝试获取大约的文档类型。 3k链接。但是当它触及 700-900 标记线时,我总是得到一个例外。
如何在异常发生的地方继续(所以我没有义务再次从零开始)?这可能吗?
这是我使用的代码:
try
{
Parallel.ForEach(linkList, link =>
{
stopwatch.Restart();
Console.Write($"Downloading page {index++} of {linkList.Count}...");
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
Console.WriteLine();
});
return documents;
}
catch (Exception e)
{
???
}
尝试将内部代码包装在 try-catch 中
Parallel.ForEach(linkList, link =>
{
try
{
stopwatch.Restart();
Console.Write($"Downloading page {index++} of {linkList.Count}...");
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
Console.WriteLine();
}
catch (Exception e)
{
???
}
});
return documents;
编辑:
您可能还想查看 thread-safe collections C# 必须提供的内容,因为普通集合不是线程安全的
您只需要在 ForEach
中处理它们
Parallel.ForEach(linkList, link =>
{
try
{
...
}
catch(Exception ex)
{
// log
}
});
然而你的问题远不止于此。
- 这看起来像是 IO 绑定工作负载,不适合
Parallel.ForEach
documents.Add
看起来不是线程安全的
- 你的索引会出来
老实说,这看起来确实像是 TPL Dataflow 的工作,它让您受益于 async 和 await和 IO 绑定工作负载。使用 async 和 await,将停止扰动任务计划程序,让 IO 完成端口完成它们的工作,释放线程池。
它还允许您制作更复杂的管道,并且能够在需要时自行将失败的作业重新送回,还有许多其他优势
好的,这就是让我实现目标的解决方案。
var index = 1;
Parallel.ForEach(linkList, link => { GetDocuments(stopwatch, index++, linkList, documents, link); });
if (FailedDownloads.Count > 0)
{
linkList = new List<string>(FailedDownloads);
FailedDownloads.Clear();
Parallel.ForEach(linkList,
link => { GetDocuments(stopwatch, index++, linkList, documents, link); });
}
return documents;
}
private void GetDocuments(Stopwatch stopwatch, int index, List<string> linkList, List<HtmlDocument> documents, string link)
{
stopwatch.Restart();
Console.Write($"Downloading page {index} of {linkList.Count}...");
try
{
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
}
catch (AggregateException e)
{
if (e.InnerExceptions[0] is HttpRequestException)
{
FailedDownloads.Add(link);
Console.WriteLine(e);
}
else
{
throw;
}
}
我正在尝试获取大约的文档类型。 3k链接。但是当它触及 700-900 标记线时,我总是得到一个例外。
如何在异常发生的地方继续(所以我没有义务再次从零开始)?这可能吗?
这是我使用的代码:
try
{
Parallel.ForEach(linkList, link =>
{
stopwatch.Restart();
Console.Write($"Downloading page {index++} of {linkList.Count}...");
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
Console.WriteLine();
});
return documents;
}
catch (Exception e)
{
???
}
尝试将内部代码包装在 try-catch 中
Parallel.ForEach(linkList, link =>
{
try
{
stopwatch.Restart();
Console.Write($"Downloading page {index++} of {linkList.Count}...");
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
Console.WriteLine();
}
catch (Exception e)
{
???
}
});
return documents;
编辑:
您可能还想查看 thread-safe collections C# 必须提供的内容,因为普通集合不是线程安全的
您只需要在 ForEach
Parallel.ForEach(linkList, link =>
{
try
{
...
}
catch(Exception ex)
{
// log
}
});
然而你的问题远不止于此。
- 这看起来像是 IO 绑定工作负载,不适合
Parallel.ForEach
documents.Add
看起来不是线程安全的- 你的索引会出来
老实说,这看起来确实像是 TPL Dataflow 的工作,它让您受益于 async 和 await和 IO 绑定工作负载。使用 async 和 await,将停止扰动任务计划程序,让 IO 完成端口完成它们的工作,释放线程池。
它还允许您制作更复杂的管道,并且能够在需要时自行将失败的作业重新送回,还有许多其他优势
好的,这就是让我实现目标的解决方案。
var index = 1;
Parallel.ForEach(linkList, link => { GetDocuments(stopwatch, index++, linkList, documents, link); });
if (FailedDownloads.Count > 0)
{
linkList = new List<string>(FailedDownloads);
FailedDownloads.Clear();
Parallel.ForEach(linkList,
link => { GetDocuments(stopwatch, index++, linkList, documents, link); });
}
return documents;
}
private void GetDocuments(Stopwatch stopwatch, int index, List<string> linkList, List<HtmlDocument> documents, string link)
{
stopwatch.Restart();
Console.Write($"Downloading page {index} of {linkList.Count}...");
try
{
documents.Add(LoadPage(link));
Console.Write($" in {stopwatch.Elapsed.TotalMilliseconds} ms");
}
catch (AggregateException e)
{
if (e.InnerExceptions[0] is HttpRequestException)
{
FailedDownloads.Add(link);
Console.WriteLine(e);
}
else
{
throw;
}
}