Reputation: 1344
I am using a Task.FromAsync and Task.ContinueWith method to complete my work. I am using a List in my program and when I run it, it gives me an Index Out of Range Exception. However, when I go through the debugger it runs through and finishes just fine. Is there something that I am missing or does Tasks operate differently when using them with a loop and a List?
public int TimerCounter = 0;
public IList<WebsiteResult>webResult = new List<WebsiteResult>();
public void sendRequest(){
foreach(Website web in TempVar._allWebsites)
{
webResult.Add(new WebSiteResult {});
try
{
pageCheck(web);
webResult.ElementAt(TimerCounter).RequestSentTime = DateTime.Now.ToString();
if(webResult.ElementAt(TimerCounter).SystemStatus == null)
webResult.ElementAt(TimerCounter).SystemStatus = "";
TimeCounter++;
}
}
public void pageCheck(){
IAsyncResult asyncResult;
Uri uri = new Uri(TempURL); //TempUrl is assigned a string beforehand
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(uri);
try{
Task<WebResponse> task = Task.Factory.FromAsync(
myReq.BeginGetResponse,
asyncResult => myReq.EndGetResponse(asyncResult),
(Object)null);
task.ContinueWith(t =>
{
var responseCode = (HttpWebResponse)t.Result;
ReadStreamFromResponse(t.Result);
if(responseCode.StatusCode == HttpStatusCode.OK){
webResult.ElementAt(TimerCounter).ResponseStatusCode = "Up"; //Error occurs here
reponseCode.Close();
}
}
);
}
//catch exceptions
}
private String ReadStreamFromResponse(WebResponse response) {
StreamReader responseStream = new StreamReader(response.GetResponseStream());
string str = responseStream.ReadToEnd();
return str;
}
Upvotes: 3
Views: 1615
Reputation: 23831
This is because your call to pageCheck()
sets up a continuation that will be run when the Task
finishes/completes. However, the call to the pageCheck()
method will return to the caller (sendRequest()
) almost immediately after setting up and spinning off your Task
. Your Task
will take time to execute on a background thread pool thread, but will return control to sendRequest()
quickly which then tries to access your IList
which will have no elements as these are populated in the continuation. This may work sometimes depending on how the Task
is dispatched by the Thread-Pool optimiser and how long the task takes. If you step through using the debugger this can effect the way the Task
is fired and can effect the order of operation - so again can effect the outcome of these operations.
My advice would be to put your
task.ContinueWith(t =>
{
var responseCode = (HttpWebResponse)t.Result;
ReadStreamFromResponse(t.Result);
if(responseCode.StatusCode == HttpStatusCode.OK)
{
webResult.ElementAt(TimerCounter).ResponseStatusCode = "Up"; //Error occurs here
reponseCode.Close();
}
webResult.ElementAt(TimerCounter).RequestSentTime = DateTime.Now.ToString();
if(webResult.ElementAt(TimerCounter).SystemStatus == null)
webResult.ElementAt(TimerCounter).SystemStatus = "";
TimeCounter++;
});
into the continuation inside pageCheck()
. The other option is to create another method which wraps this process in a 'higher-level task', to you would set up your Task
and the continuation in the sendRequest()
method instead.
Note, using a lock in this case will not helps you as you cannot tell when the lock will be imposed. It will likely not effect the order of execution and your problem will remain.
I hope this helps.
Upvotes: 2