Parallel.For 和 httpclient 使应用程序 C# 崩溃
Parallel.For and httpclient crash the application C#
我想避免由于并行 for 循环和 httpclient 而导致的应用程序崩溃问题,但由于我的编程知识有限,我无法应用网络上其他地方提供的解决方案。我的代码粘贴在下面。
class Program
{
public static List<string> words = new List<string>();
public static int count = 0;
public static string output = "";
private static HttpClient Client = new HttpClient();
public static void Main(string[] args)
{
//input path strings...
List<string> links = new List<string>();
links.AddRange(File.ReadAllLines(input));
List<string> longList = new List<string>(File.ReadAllLines(@"a.txt"));
words.AddRange(File.ReadAllLines(output1));
System.Net.ServicePointManager.DefaultConnectionLimit = 8;
count = longList.Count;
//for (int i = 0; i < longList.Count; i++)
Task.Run(() => Parallel.For(0, longList.Count, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i, loopState) =>
{
Console.WriteLine(i);
string link = @"some link" + longList[i] + "/";
try
{
if (!links.Contains(link))
{
Task.Run(async () => { await Download(link); }).Wait();
}
}
catch (System.Exception e)
{
}
}));
//}
}
public static async Task Download(string link)
{
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
document.LoadHtml(await getURL(link));
//...stuff with html agility pack
}
public static async Task<string> getURL(string link)
{
string result = "";
HttpResponseMessage response = await Client.GetAsync(link);
Console.WriteLine(response.StatusCode);
if(response.IsSuccessStatusCode)
{
HttpContent content = response.Content;
var bytes = await response.Content.ReadAsByteArrayAsync();
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
有解决方案,例如 this one,但我不知道如何在我的 main 方法中放置 await
关键字,目前程序由于在 [=13] 之前不存在而直接退出=].如您所见,我已经应用了关于 async Download()
方法的变通方法以在 main 方法中调用它。
我也怀疑在不同的并行线程中使用相同的 httpclient 实例。请告诉我是否应该每次都创建新的 httpclient 实例。
你是对的,你必须在控制台应用程序中的某个地方阻止任务,否则程序将在完成之前退出。但是你做的比你需要做的更多。旨在仅阻塞主线程并将其余部分委托给 async
方法。一个好的做法是创建一个带有 private async Task MainAsyc(args)
签名的方法,将程序逻辑的 "guts" 放在那里,从 Main
调用它,如下所示:
MainAsync(args).Wait();
在您的示例中,将所有内容从 Main
移动到 MainAsync
。然后你就可以随意使用 await
了。 Task.Run
和 Parallel.For
显式地为 I/O 绑定工作消耗新线程,这在异步世界中是不必要的。请改用 Task.WhenAll
。 MainAsync
方法的最后一部分应该看起来像这样:
await Task.WhenAll(longList.Select(async s => {
Console.WriteLine(i);
string link = @"some link" + s + "/";
try
{
if (!links.Contains(link))
{
await Download(link);
}
}
catch (System.Exception e)
{
}
}));
虽然这里有一个小问题。您的示例将并行度限制在 5。如果您发现您仍然需要它,TPL Dataflow 是一个很棒的库,用于在异步世界中限制并行度。 .
关于HttpClient,跨线程使用单实例是completely safe and highly encouraged。
我想避免由于并行 for 循环和 httpclient 而导致的应用程序崩溃问题,但由于我的编程知识有限,我无法应用网络上其他地方提供的解决方案。我的代码粘贴在下面。
class Program
{
public static List<string> words = new List<string>();
public static int count = 0;
public static string output = "";
private static HttpClient Client = new HttpClient();
public static void Main(string[] args)
{
//input path strings...
List<string> links = new List<string>();
links.AddRange(File.ReadAllLines(input));
List<string> longList = new List<string>(File.ReadAllLines(@"a.txt"));
words.AddRange(File.ReadAllLines(output1));
System.Net.ServicePointManager.DefaultConnectionLimit = 8;
count = longList.Count;
//for (int i = 0; i < longList.Count; i++)
Task.Run(() => Parallel.For(0, longList.Count, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i, loopState) =>
{
Console.WriteLine(i);
string link = @"some link" + longList[i] + "/";
try
{
if (!links.Contains(link))
{
Task.Run(async () => { await Download(link); }).Wait();
}
}
catch (System.Exception e)
{
}
}));
//}
}
public static async Task Download(string link)
{
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
document.LoadHtml(await getURL(link));
//...stuff with html agility pack
}
public static async Task<string> getURL(string link)
{
string result = "";
HttpResponseMessage response = await Client.GetAsync(link);
Console.WriteLine(response.StatusCode);
if(response.IsSuccessStatusCode)
{
HttpContent content = response.Content;
var bytes = await response.Content.ReadAsByteArrayAsync();
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
有解决方案,例如 this one,但我不知道如何在我的 main 方法中放置 await
关键字,目前程序由于在 [=13] 之前不存在而直接退出=].如您所见,我已经应用了关于 async Download()
方法的变通方法以在 main 方法中调用它。
我也怀疑在不同的并行线程中使用相同的 httpclient 实例。请告诉我是否应该每次都创建新的 httpclient 实例。
你是对的,你必须在控制台应用程序中的某个地方阻止任务,否则程序将在完成之前退出。但是你做的比你需要做的更多。旨在仅阻塞主线程并将其余部分委托给 async
方法。一个好的做法是创建一个带有 private async Task MainAsyc(args)
签名的方法,将程序逻辑的 "guts" 放在那里,从 Main
调用它,如下所示:
MainAsync(args).Wait();
在您的示例中,将所有内容从 Main
移动到 MainAsync
。然后你就可以随意使用 await
了。 Task.Run
和 Parallel.For
显式地为 I/O 绑定工作消耗新线程,这在异步世界中是不必要的。请改用 Task.WhenAll
。 MainAsync
方法的最后一部分应该看起来像这样:
await Task.WhenAll(longList.Select(async s => {
Console.WriteLine(i);
string link = @"some link" + s + "/";
try
{
if (!links.Contains(link))
{
await Download(link);
}
}
catch (System.Exception e)
{
}
}));
虽然这里有一个小问题。您的示例将并行度限制在 5。如果您发现您仍然需要它,TPL Dataflow 是一个很棒的库,用于在异步世界中限制并行度。
关于HttpClient,跨线程使用单实例是completely safe and highly encouraged。