0

On ASP.NET MVC, I try to write an async Controller action with the old asynchronous programming model (actually, it is the current one, new one is still a CTP).

Here, I am trying to run 4 operations in parallel and it worked great. Here is the complete code:

public class SampleController : AsyncController {

    public void IndexAsync() {

        AsyncManager.OutstandingOperations.Increment(4);

        var task1 = Task<string>.Factory.StartNew(() => {

            return GetReponse1();
        });
        var task2 = Task<string>.Factory.StartNew(() => {

            return GetResponse2();
        });
        var task3 = Task<string>.Factory.StartNew(() => {

            return GetResponse3();
        });
        var task4 = Task<string>.Factory.StartNew(() => {

            return GetResponse4();
        });

        task1.ContinueWith(t => {

            AsyncManager.Parameters["headers1"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        task2.ContinueWith(t => {

            AsyncManager.Parameters["headers2"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        task3.ContinueWith(t => {

            AsyncManager.Parameters["headers3"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        task4.ContinueWith(t => {

            AsyncManager.Parameters["headers4"] = t.Result;
            AsyncManager.OutstandingOperations.Decrement();
        });

        task3.ContinueWith(t => {

            AsyncManager.OutstandingOperations.Decrement();

        }, TaskContinuationOptions.OnlyOnFaulted);
    }

    public ActionResult IndexCompleted(string headers1, string headers2, string headers3, string headers4) {

        ViewBag.Headers = string.Join("<br/><br/>", headers1, headers2, headers3, headers4);

        return View();
    }

    public ActionResult Index2() {

        ViewBag.Headers = string.Join("<br/><br/>", GetReponse1(), GetResponse2(), GetResponse3(), GetResponse4());

        return View();
    }
}

And these below ones are the methods that the async operations are running:

string GetReponse1() {

    var req = (HttpWebRequest)WebRequest.Create("http://www.twitter.com");
    req.Method = "HEAD";

    var resp = (HttpWebResponse)req.GetResponse();

    return FormatHeaders(resp.Headers);
}

string GetResponse2() {

    var req2 = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com");
    req2.Method = "HEAD";

    var resp2 = (HttpWebResponse)req2.GetResponse();

    return FormatHeaders(resp2.Headers);
}

string GetResponse3() {

    var req = (HttpWebRequest)WebRequest.Create("http://google.com");
    req.Method = "HEAD";

    var resp = (HttpWebResponse)req.GetResponse();

    return FormatHeaders(resp.Headers);
}

string GetResponse4() {

    var req = (HttpWebRequest)WebRequest.Create("http://github.com");
    req.Method = "HEAD";

    var resp = (HttpWebResponse)req.GetResponse();

    return FormatHeaders(resp.Headers);
}

private static string FormatHeaders(WebHeaderCollection headers) {

    var headerStrings = from header in headers.Keys.Cast<string>()
                        select string.Format("{0}: {1}", header, headers[header]);

    return string.Join("<br />", headerStrings.ToArray());
}

I have also Index2 method here which is synchronous and does the same thing.

I compare two operation execution times and there is major difference (approx. 2 seconds)

But I think I am missing lots of things here (exception handling, timeouts, etc). I only implement the exception handling on task3 but I don't think it is the right way of doing that.

What is the healthiest way of handling exceptions for this kind of operations?

1

1 Answer 1

3

Before accessing the result of a task you should test whether it completed successfully. If there was an error you should not try to access the results but instead do something with the exception:

task1.ContinueWith(t => 
{
    if (!t.IsFaulted)
    {
        AsyncManager.Parameters["headers1"] = t.Result;
    }
    else if (t.IsFaulted && t.Exception != null)
    {
        AsyncManager.Parameters["error"] = t.Exception;
    }
    AsyncManager.OutstandingOperations.Decrement();
});
Sign up to request clarification or add additional context in comments.

1 Comment

I see now. I tried to access the result of task when I throw an exception on purpose and that's why the operations just ran endlessly. Do you think that I will be 100% safe with this approach and be able to throw appropriate exceptions? Also, do you think if I should implement AsyncTimeout attribute here?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.