Чистая обработка AsyncTimeout на ASP.NET Async Page

По этой статье

Обработчик события Begin всегда вызывается

Второе значение AsyncTimeout, которое я до недавнего времени не усвоил, заключается в том, что всегда запускается обработчик события начала для зарегистрированной асинхронной задачи, даже если время ожидания страницы истекло до того, как ASP.NET приступил к запуску этой задачи.

В этом сценарии остальные задачи в основном обречены. Время ожидания страницы уже истекло, но ASP.NET по-прежнему собирается выполнить вызов обработчика события begin для всех зарегистрированных задач, а затем немедленно вызвать соответствующий обработчик события тайм-аута.

AsyncTimeout не подразумевает отмену асинхронной задачи

где это возможно, я должен помнить, чтобы не запускать дополнительные задачи после того, как тайм-аут страницы уже истек, и я должен отменить оставшиеся запущенные задачи, как только истечет время ожидания. Это рекомендуется / безопасно? Как бы я пошел о полной отмене любых вызовов HttpWebRequest.BeginGetResponse, если есть несколько мест, где может быть асинхронная задача? Например, если я нахожусь внутри вызова BeginGetResponse, что я должен вернуть для IAsyncResult, чтобы безопасно остановить обработку своих треков? Будут ли такие же рекомендации применимы к SqlCommand.BeginExecuteReader?

Я собрал пример кода из нескольких примеров и моих собственных дополнений:

<%@ Page Language="C#" Async="true" AsyncTimeout="30" %>

<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Threading" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected class RequestState
    {
        public HttpWebRequest Request;
        public string RequestID; // simple identifier of the request
        public string Url;
        public DateTime RequestStartTime;

        public RequestState(string requestID, string url)
        {
            this.RequestID = requestID;
            this.Url = url;
        }

        public void CreateRequest()
        {
            Request = (HttpWebRequest)WebRequest.Create(Url + pageDest);
            Request.Method = "GET";
            Request.Proxy = WebRequest.DefaultWebProxy;
            Request.Timeout = 1000 * 3; // Connection timeout
            Request.ReadWriteTimeout = 1000 * 15; // Read response timeout

            RequestStartTime = DateTime.Now; // Technically the request didn't start yet, but this is close enough
        }
    }

    protected class ResponseDetails
    {
        public string RequestID;
        public string Url;
        public string ServerID;
        public double Duration;
        public string ResponseString;

        public string FormattedDuration
        {
            get
            {
                return String.Format("{0:N0}ms", Duration);
            }
        }

        public bool IsServerOk
        {
            get
            {
                // trivial check, better check intended for real implementation
                return ResponseString.Contains("<body");
            }
        }

        public ResponseDetails(RequestState reqState, string serverID, string responseString)
        {
            this.RequestID = reqState.RequestID;
            this.ServerID = serverID;
            this.Url = reqState.Url;
            this.ResponseString = responseString;
            this.Duration = (DateTime.Now - reqState.RequestStartTime).TotalMilliseconds;
        }
    }

    public const string pageDest = "/";
    Dictionary<string, string> serverRequests = new Dictionary<string, string>()
    {
      { "dev1", "http://www.yahoo.com" },
      { "dev2", "http://www.msn.com" },
      { "dev3", "http://www.google.com" }
    };
    Dictionary<string, ResponseDetails> serverResponses = new Dictionary<string, ResponseDetails>();
    object dictLock = new object();
    int maxIOThreads = 0;
    int maxWorkerThreads = 0;

    protected bool ShowThreadingInfo = false;
    protected bool ShowRequestTime = true;
    protected bool ExecuteInParallel = true;

    private string GetThreadingCounts()
    {
        StringBuilder sb = new StringBuilder();

        sb.AppendFormat("<b>EndIOCPUpDate {0}</b><br />", DateTime.Now);
        /*
                sb.AppendFormat("CompletedSynchronously: {0}<br/><br/>" + AR.CompletedSynchronously + "<br /><br />");

                sb.AppendFormat("isThreadPoolThread: " + System.Threading.Thread.CurrentThread.IsThreadPoolThread.ToString() + "<br />";

                sb.AppendFormat("ManagedThreadId : " + System.Threading.Thread.CurrentThread.ManagedThreadId + "<br />";

                sb.AppendFormat("GetCurrentThreadId : " + AppDomain.GetCurrentThreadId() + "<br />";

                sb.AppendFormat("Thread.CurrentContext : " + System.Threading.Thread.CurrentContext.ToString() + "<br />";
        */

        int availWorker = 0;
        int maxWorker = 0;
        int availCPT = 0;
        int maxCPT = 0;

        ThreadPool.GetAvailableThreads(out availWorker, out availCPT);
        ThreadPool.GetMaxThreads(out maxWorker, out maxCPT);

        if (maxIOThreads < (maxCPT - availCPT))
            maxIOThreads = (maxCPT - availCPT);
        if (maxWorkerThreads < (maxWorker - availWorker))
            maxWorkerThreads = (maxWorker - availWorker);

        sb.AppendFormat("--Available Worker Threads: {0}<br/>", availWorker);
        sb.AppendFormat("--Maximum Worker Threads: {0}<br/>", maxWorker);
        sb.AppendFormat("--Available Completion Port Threads: {0}<br/>", availCPT);
        sb.AppendFormat("--Maximum Completion Port Threads: {0}<br/>", maxCPT);
        sb.AppendFormat("===========================<br /><br />");

        return sb.ToString();
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        foreach (KeyValuePair<string, string> kvp in serverRequests)
        {
            RequestState reqState = new RequestState(kvp.Key, kvp.Value);

            Page.RegisterAsyncTask(new PageAsyncTask(new BeginEventHandler(this.BeginGetStatusPage),
            new EndEventHandler(this.EndGetStatusPage), new EndEventHandler(this.TimeoutHandler), reqState, ExecuteInParallel));
        }
    }

    protected override void OnPreRenderComplete(EventArgs e)
    {
        base.OnPreRenderComplete(e);
        // write the result messages to the Label.
        MessageOut();
    }

    private IAsyncResult BeginGetStatusPage(Object sender, EventArgs e, AsyncCallback cb, object state)
    {
        RequestState reqState = (RequestState)state;

        AddTraceMessage("Begin " + reqState.Url);

        reqState.CreateRequest();

        return reqState.Request.BeginGetResponse(cb, reqState);
    }

    void EndGetStatusPage(IAsyncResult asyncResult)
    {
        AddTraceMessage("EndAsync");

        if (asyncResult != null)
        {
            RequestState reqState = asyncResult.AsyncState as RequestState;

            AddTraceMessage("End " + reqState.Url);

            string serverKey = null;
            string retString = null;
            StringBuilder sb = new StringBuilder();

            try
            {
                using (WebResponse response1 = (WebResponse)reqState.Request.EndGetResponse(asyncResult))
                {
                    AddTraceMessage("End Response " + reqState.Url);

                    // grab a custom header later, not useful yet
                    serverKey = response1.Headers["Server"];

                    // we will read data via the response stream
                    using (Stream resStream = response1.GetResponseStream())
                    {
                        using (StreamReader rdr = new StreamReader(resStream))
                        {
                            sb.Append(rdr.ReadToEnd());
                            rdr.Close();
                        }
                        resStream.Close();
                    }
                    response1.Close();
                }
            }
            catch (WebException ex)
            {
                sb.AppendLine(ex.Status.ToString() + ": " + ex.Message);
            }
            retString = sb.ToString();

            AddTraceMessage("End Response2 " + reqState.Url);

            ResponseDetails rd = new ResponseDetails(reqState, serverKey, retString);

            UpdateServerResponses(rd);
        }
    }

    void UpdateServerResponses(ResponseDetails details)
    {
        lock (dictLock)
        {
            serverResponses.Add(details.RequestID, details);
        }
    }

    // This doesn't actually cancel the task I don't think
    void TimeoutHandler(IAsyncResult asyncResult)
    {
        AddTraceMessage("Request Timed Out");
        // Aborts one request running during page timeout.  What about the rest??
        RequestState reqState = asyncResult.AsyncState as RequestState;
        reqState.Request.Abort();

        ShowErrorDetails("<span style='color:red;font-weight:bold'>Timed out:</span> " + reqState.RequestID + "<br/><br/>");
    }

    private void ShowErrorDetails(ResponseDetails rd)
    {
        PanelErrors.Visible = true;
        LabelErrors.Text += String.Format("<span style='color:blue;font-weight:bold'>{0}</span><br/>{1}<br/><br/>", rd.RequestID, rd.ResponseString);
    }

    private void ShowErrorDetails(string message)
    {
        PanelErrors.Visible = true;
        LabelErrors.Text += message;
    }
    private void MessageOut()
    {
        PanelThreading.Visible = ShowThreadingInfo;

        Page.Trace.Write(_trace.ToString());

        foreach (KeyValuePair<string, string> kvp in serverRequests)
        {
            string respStr = "<font color='red'>No Reply</font>";

            if (serverResponses.ContainsKey(kvp.Key))
            {
                ResponseDetails rd = serverResponses[kvp.Key];

                if (rd.IsServerOk)
                {
                    respStr = "OK";
                    respStr += " (" + rd.ServerID + ")";
                }
                else
                {
                    ShowErrorDetails(rd);
                    respStr = "<font color='red'>ERROR</font>";
                }

                //In parallel task mode it seems to return the time of the single longest individual request.  Really weird.
                if (ShowRequestTime)
                    respStr += " [" + rd.FormattedDuration + "]";
            }

            this.Label1.Text += String.Format("{0}: {1}<br/>", kvp.Key, respStr);
        }

        this.Label1.Text += String.Format("<br/><b>MaxWorkerThreads:</b> {0}<br/>", maxWorkerThreads);
        this.Label1.Text += String.Format("<b>MaxIOThreads:</b> {0}<br/>", maxIOThreads);
    }

    StringBuilder _trace = new StringBuilder();
    DateTime _pageStartTime = DateTime.Now;

    public void AddTraceMessage(string message)
    {
        double t = (DateTime.Now - _pageStartTime).TotalSeconds;

        lock (_trace)
        {
            _trace.AppendFormat("Thread:[{0:000}] {1:00.000} -- {2}\r\n",

            System.Threading.Thread.CurrentThread.GetHashCode(), t, message);

            LabelThreading.Text += GetThreadingCounts();
        }
    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label runat="server" ID="Label1"></asp:Label>
        <asp:Panel runat="server" ID="PanelErrors" Visible="false">
            <br />
            <h2>
                Error Details:</h2>
            <br />
            <asp:Label runat="server" ID="LabelErrors"></asp:Label>
        </asp:Panel>
        <asp:Panel runat="server" ID="PanelThreading" Visible="true">
            <br />
            <h2>
                Threading Details:</h2>
            <br />
            <asp:Label runat="server" ID="LabelThreading"></asp:Label>
        </asp:Panel>
    </div>
    </form>
</body>
</html>

Обновление: есть ли другая информация, которую я должен предоставить?

0 ответов

Другие вопросы по тегам