Solved:
I found the solution to this.See Joe Enzminger's reply for an explanation
Thank you all again. ps. the code is used for playing pool (billiards) online - windows (free) version here for anyone curious and brave enough to try :)
Hello,
I've implemented an IHttpAsyncHandler that client applications "poll" to wait for server notifications. Notifications are generated by other "activities" on the server and the Async Handler does no work at all.
The execution steps are:
IHttpAsyncHandler.BeginProcessRequest
Create AsyncResult instance and add it to a "registered clients" collection
return the AsyncResult
...other server activity will generate notifications to be sent to registered clients...
AsyncResult.CompleteCall called as a result of the generated notification(s).
IHttpAsyncHandler.EndProcessRequest is called
The notification(s) attached to the AsyncResult are written to the response stream.
The problem:
I've tested this on IIS7 on a VM with Windows Server 2008 SP2 and 1 cpu core. After 12 clients register for notifications (using an HTTP GET on the Async.ashx) the performance is degraded to the point that subsequent clients cannot connect.
When I check the ASP.NET performance counters the "Requests Executing" counter goes up with each client registration and stays at 12 (which appears to be its maximum value - probably a thread pool size per CPU).
I find this very confusing. I though the whole point of async handlers is to free up threads for other connections. It appears that this is not the case so I must be doing something wrong!
Why is ASP.NET consuming a thread while waiting for my AsyncResult to complete? Is this a config issue? Do I need to do something specific to indicate that this is an Async Handler?
Thank you, Nikos.
Edit: Added code below:
public class AsyncResult : IAsyncResult
{
private AsyncCallback _cb;
private object _state;
private ManualResetEvent _event;
private bool _completed;
private bool _completedsynchronously;
private HttpContext _context;
private byte[] _data;
private int _datalength;
private object _lock = new object();
public AsyncWaitResult(AsyncCallback cb, object state, HttpContext context)
{
_context = context;
_cb = cb;
_state = state;
}
public void Close()
{
if (_event != null)
{
_event.Close();
_event = null;
}
}
public HttpContext Context { get { return _context; } }
public Object AsyncState { get { return _state; } }
public bool CompletedSynchronously { get { return _completedsynchronously; } }
public bool IsCompleted { get { return _completed; } }
public byte[] Data { get { return _data; } }
public int DataLength { get { return _datalength; } }
public WaitHandle AsyncWaitHandle
{
get
{
lock (_lock)
{
if (_event == null)
_event = new ManualResetEvent(_completed);
return _event;
}
}
}
public void CompleteCall(byte[] data, int length, bool completedsynchronously)
{
_data = data;
_datalength = length;
_completedsynchronously = completedsynchronously;
lock (_lock)
{
_completed = true;
if (_event != null)
_event.Set();
}
if (_cb != null)
_cb(this);
}
}
public class Outbound : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object state开发者_如何学Python)
{
AsyncResult asyncresult = new AsyncResult(cb, state, context);
RegisteredClients.Instance.Add(asyncresult);
return asyncresult;
}
public void EndProcessRequest(IAsyncResult ar)
{
AsyncResult result = (AsyncResult)ar;
if (result != null)
{
result.Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
result.Context.Response.ContentType = "application/octet-stream";
result.Context.Response.AddHeader("Connection", "keep-alive");
if (result.Data != null)
result.Context.Response.OutputStream.Write(result.Data, 0, result.DataLength);
result.Close();
}
}
public void ProcessRequest(HttpContext context){}
public bool IsReusable { get { return true; } }
}
Here is a blog post that explains what you are seeing:
http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx
and companion post
http://blogs.msdn.com/b/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
In integrated pipeline mode, using the default configuration, IIS7 places a limit of 12 concurrent REQUESTS (not threads) per CPU. You can change this by modifying the configuration.
I couldn't let it go. I'm pretty sure this is what you're seeing. Deep diving into the article, I don't really like the change they made because it clearly causes problems like this, but who am I to judge!
Another thing to check. If your client is not an actual browser but rather another application that is making multiple concurrent requests to your server this could cause your issue.
Concurrent Requests and Session State
Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.
精彩评论