Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC AsyncController and IO-bound requests

I have an AsyncController and a homepage that queries the user's friends list and does some database work with them. I implemented the async action method pattern for any requests that call external web services. Is this an efficient way of handling this situation? During times of high request volume I am seeing IIS being thread-starved at times and I worry that my nested Async magic may somehow be involved in this.

My main questions/talking points are:

  • Is it safe to nest an IAsyncResult asynchronous web request inside an Async controller action? Or is this just doubling the load somewhere?
  • Is it efficient to use ThreadPool.RegisterWaitForSingleObject to handle timing out long running web requests or will this eat ThreadPool threads and starve the rest of the app?
  • Would it be more efficient to just do a synchronous web request inside an Async Controller action?

Example code:

public void IndexAsync() 
{
    AsyncManager.OutstandingOperations.Increment();

    User.GetFacebookFriends(friends => {

        AsyncManager.Parameters["friends"] = friends;

        AsyncManager.OutstandingOperations.Decrement();
    });
}

public ActionResult IndexCompleted(List<Friend> friends)
{
    return Json(friends);
}

User.GetFacebookFriends(Action<List<Friend>>) looks like this:

void GetFacebookFriends(Action<List<Friend>> continueWith) {

    var url = new Uri(string.Format("https://graph.facebook.com/etc etc");

    HttpWebRequest wc = (HttpWebRequest)HttpWebRequest.Create(url);

    wc.Method = "GET";

    var request = wc.BeginGetResponse(result => QueryResult(result, continueWith), wc);

    // Async requests ignore the HttpWebRequest's Timeout property, so we ask the ThreadPool to register a Wait callback to time out the request if needed
    ThreadPool.RegisterWaitForSingleObject(request.AsyncWaitHandle, QueryTimeout, wc, TimeSpan.FromSeconds(5), true);
}

QueryTimeout just aborts the request if it takes longer than 5 seconds.

like image 831
Foritus Avatar asked Jan 25 '26 13:01

Foritus


1 Answers

The fully asynchronous method you first describe is best, as this releases TP threads back to the pool for reuse. It's highly likely that you're performing some other blocking action elsewhere. What happens in QueryResponse? Although you fetch the response asynchronously, are you also reading the response stream asynchronously? If not, make it so, then TP starvation should be reduced.

like image 125
spender Avatar answered Jan 28 '26 03:01

spender



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!