This is the first time using the Task Parallel Library and it seems to be performing much slower than I was expecting. The application is simply a web crawler that returns all URLs for a given URL.
Here is my method that, depending on the level
param, will either download single page links (getSinglePageLinks()
) or it will make use of my threaded method (getManyPageLinks()
):
public static IEnumerable<string> getLinks(string url, bool hostMatch=true, bool validatePages=true, int level=0)
{
string formattedUrl = urlFormatValidator(url);
if (string.IsNullOrEmpty(formattedUrl)) return Enumerable.Empty<string>();
//download root url's
IEnumerable<string> rootUrls = getSinglePageLinks(formattedUrl, hostMatch, validatePages);
//download url's for each level
for (int i=0; i<level; i++)
{
rootUrls = rootUrls.Union(getManyPageLinks(rootUrls, hostMatch, validatePages));
}
return rootUrls;
}
getSinglePageLinks()
makes use of the HTML agility pack, and it simply downloads and parses the given URL:
private static IEnumerable<string> getSinglePageLinks(string formattedUrl, bool hostMatch = true, bool validatePages = true)
{
try
{
HtmlDocument doc = new HtmlWeb().Load(formattedUrl);
var linkedPages = doc.DocumentNode.Descendants("a")
.Select(a => a.GetAttributeValue("href", null))
.Where(u => !String.IsNullOrEmpty(u))
.Distinct();
//hostMatch and validatePages left out
return linkedPages;
}catch(...){...}
And my getManyPageLinks()
:
private static IEnumerable<string> getManyPageLinks(IEnumerable<string> rootUrls, bool hostMatch, bool validatePages)
{
List<Task> tasks = new List<Task>();
List<List<string>> allLinks = new List<List<string>>();
foreach (string rootUrl in rootUrls)
{
string rootUrlCopy = rootUrl; //required
var task = Task.Factory.StartNew(() =>
{
IEnumerable<string> taskResult = getSinglePageLinks(rootUrlCopy, hostMatch, validatePages);
return taskResult;
});
tasks.Add(task);
allLinks.Add(task.Result.ToList());
}
Task.WaitAll(tasks.ToArray());
return allLinks.SelectMany(x => x).Distinct();
}
The app works OK if the level is set to 0, but if I set the level to 1 so it gets all links for all of the roots URL, the CPU/Network usage does not go above 1-3%. How can I improve performance?