TheProgrammer
TheProgrammer

Reputation: 1344

Index Out of Range Exception Using Tasks

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

Answers (1)

MoonKnight
MoonKnight

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

Related Questions