I am working on an asp.net MVC-5 web application, and based on some articles i read that i should not use Parallel methods inside web servers and inside .net web applications espically.
now in my case i have around 1,500 WebClient()
calls that i need to issue inside a foreach, and then deserialize the return json object from the WebClient()
calls. my original code before using Parallel.Foreach
was as follow, which took around 15 minutes to complete:-
public async Task <List<Details2>> Get()
{
try
{
using (WebClient wc = new WebClient())
{
string url = currentURL + "resources?AUTHTOKEN=" + pmtoken;
var json = await wc.DownloadStringTaskAsync(url);
resourcesinfo = JsonConvert.DeserializeObject<ResourcesInfo>(json);
}
ForEach( var c in resourcesinfo.operation.Details)
{
ResourceAccountListInfo resourceAccountListInfo = new ResourceAccountListInfo();
using (WebClient wc = new WebClient())
{
string url = currentURL + "resources/" + c.RESOURCEID + "/accounts?AUTHTOKEN=" + pmtoken;
string tempurl = url.Trim();
var json = await wc.DownloadStringTaskAsync(tempurl);
resourceAccountListInfo = JsonConvert.DeserializeObject<ResourceAccountListInfo>(json);
}
if (resourceAccountListInfo.operation.Details.CUSTOMFIELD.Count > 0)
{
List<CUSTOMFIELD> customfield = resourceAccountListInfo.operation.Details.CUSTOMFIELD.Where(a =>
a.CUSTOMFIELDLABEL.ToLower() == "name"
).ToList();
if (customfield.Count == 1)
{
PMresourcesOnly.Add(resourceAccountListInfo.operation.Details);
}
}
}//end of foreach
return PMresourcesOnly.ToList();
}
catch (Exception e)
{
}
return new List<Details2>();
}
now i did the following modifications :-
- i replace
foreach
withParallel.ForEach
since i should not use async methods inside
Parallel.ForEach
so i chnage theDownloadStringTaskAsync
toDownloadString
inside theParallel.Foreach
:-public async Task <List<Details2>> Get() { try { using (WebClient wc = new WebClient()) { string url = currentURL + "resources?AUTHTOKEN=" + pmtoken; var json = await wc.DownloadStringTaskAsync(url); resourcesinfo = JsonConvert.DeserializeObject<ResourcesInfo>(json); } Parallel.ForEach(resourcesinfo.operation.Details, new ParallelOptions { MaxDegreeOfParallelism = 7 }, (c) => { ResourceAccountListInfo resourceAccountListInfo = new ResourceAccountListInfo(); using (WebClient wc = new WebClient()) { string url = currentURL + "resources/" + c.RESOURCEID + "/accounts?AUTHTOKEN=" + pmtoken; string tempurl = url.Trim(); var json = wc.DownloadString(tempurl); resourceAccountListInfo = JsonConvert.DeserializeObject<ResourceAccountListInfo>(json); } if (resourceAccountListInfo.operation.Details.CUSTOMFIELD.Count > 0) { List<CUSTOMFIELD> customfield = resourceAccountListInfo.operation.Details.CUSTOMFIELD.Where(a => a.CUSTOMFIELDLABEL.ToLower() == "name" ).ToList(); if (customfield.Count == 1) { PMresourcesOnly.Add(resourceAccountListInfo.operation.Details); } } });//end of foreach return PMresourcesOnly.ToList(); } catch (Exception e) { } return new List<Details2>(); }
now when i use the Parallel.Foreach
the execution time was reduced from 15 minutes to around 7 minutes. but i am a bit confused if my second method is valid , so can anyone adivce on these questions (or any question):-
is using
Parallel.Foreach
withWebclient()
a valid approach to follow ? or i should avoid using Parallel methods inside .net and web applications?when using
Parallel.Foreach
could i face any problem such as that thereturn PMresourcesOnly.ToList();
is return to the client while there are still somewc.DownloadString(tempurl);
that did not complete?if i want to compare the 2 methods (Parallel.Foreach & Foreach) will the result be the same ?
on some online articles they use
Task.Factory.StartNew(()
instead of usingParallel.foreach
so what are the main differences between them ?
EDIT
I tried defining the SemaphoreSlim
as follow:-
public async Task <List<Details2>> Get()
{
SemaphoreSlim throttler = new SemaphoreSlim(initialCount: 15);
try
{
//code goes here
var tasks = resourcesinfo.operation.Details.Select(c => TryDownloadResourceAsync(c.RESOURCEID,throttler)).ToList();
}
///---
private async Task<Details2> TryDownloadResourceAsync(string resourceId, SemaphoreSlim throttler)
{
await throttler.WaitAsync();
try
{
using (WebClient wc = new WebClient()) //get the tag , to check if there is a server with the same name & tag..
{}
}
finally
{
throttler.Release();
}