• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            道。道。道

            安全特性不等于安全的特性

               :: 首頁 :: 聯系 :: 聚合  :: 管理

            常用鏈接

            搜索

            •  

            最新評論

              ASP.NET 2.0 提供了大量新功能,其中包括聲明性數據綁定和母版頁,成員和角色管理服務等。但我認為最棒的功能是異步頁,接下來讓我告訴您其中的原因。
              當 ASP.NET 接收針對頁的請求時,它從線程池中提取一個線程并將請求分配給該線程。一個普通的(或同步的)頁在該請求期間保留線程,從而防止該線程用于處理其他請求。如果一個同步請求成為 I/O 綁定(例如,如果它調用一個遠程 Web 服務或查詢一個遠程數據庫,并等待調用返回),那么分配給該請求的線程在調用返回之前處于掛起狀態。這影響了可伸縮性,原因是線程池的可用線程是有限的。如果所有請求處理線程全部阻塞以等待 I/O 操作完成,則其他請求排入隊列等待線程釋放。最好的情況是吞吐量減少,因為請求等待較長的時間才能得到處理。最壞的情況則是該隊列填滿,并且 ASP.NET 因 503“Server Unavailable”錯誤使后續請求失敗。

              異步頁為由 I/O 綁定的請求引起的問題提供優秀的解決方案。頁處理從線程池線程開始,但是當一個異步 I/O 操作開始響應 ASP.NET 的信號之后,該線程返回線程池。當該操作完成時,ASP.NET 從線程池提取另一個線程,并完成該請求的處理。由于線程池線程得到了更高效的使用,因此提高了可伸縮性。那些掛起等待 I/O 完成的線程現在可用于服務其他請求。直接的受益方是不執行長時間 I/O 操作并因此可以快速進出管線的請求。長時間等待進入管線會對此類請求的性能帶來不小的負面影響。
              ASP.NET 2.0 Beta 2 異步頁基礎結構的相關文檔很少。讓我們展望一下異步頁的前景,從而彌補這點不足。請記住,本專欄涉及 ASP.NET 2.0 和 .NET Framework 2.0 的測試版本。。

              ASP.NET 1.x 中的異步頁

              ASP.NET 1.x 本質上不支持異步頁,但是通過堅韌的努力和不懈地創新可以生成異步頁。有關更多概述信息,請參閱相關資料

              這里的技巧是,在一個頁的代碼隱藏類中實現 IhttpAsyncHandler,從而提示 ASP.NET 通過調用 IHttpAsyncHandler.BeginProcessRequest 來處理請求,而不是通過調用該頁的 IHttpHandler.ProcessRequest 方法。然后,您的 BeginProcessRequest 實現可以啟動另一個線程。該線程調用 base.ProcessRequest,使得頁進入其常規請求處理生命周期(完成諸如 Load 和 Render 的事件),但是在非 ThreadPool 線程上例外。同時,啟動新線程之后 BeginProcessRequest 立即返回,從而允許執行 BeginProcessRequest 的線程返回線程池。

              這是基本思想,但細節中還有很多注意事項。其中,您需要實現 IAsyncResult,并從 BeginProcessRequest 中返回它。這通常意味著創建一個 ManualResetEvent 對象,并且當 ProcessRequest 在后臺線程中返回時向其發送信號。此外,您必須提供調用 base.ProcessRequest 的線程。遺憾的是,多數用于將工作移到后臺線程的常規技術(包括 Thread.Start、ThreadPool.QueueUserWorkItem 和異步委托)在 ASP.NET 應用程序中都是起反作用的,因為它們或者從線程池“偷盜”線程,或者有不受限制的線程增長的危險。正確的異步頁實現使用自定義線程池,但自定義線程池類不容易編寫。

              主要是在 ASP.NET 1.x 中生成異步頁并非不可能,而是有些乏味。在嘗試一、兩次之后,您不禁會想一定會有更好的方法。目前,這個好方法就是 ASP.NET 2.0。

              ASP.NET 2.0 中的異步頁

              ASP.NET 2.0 極大地簡化了生成異步頁的方式。首先使用該頁的 @ Page 指令引入 Async=“true” 屬性,如下所示:

              在后臺,這會通知 ASP.NET 在該頁中實現 IhttpAsyncHandler。接下來,您在該頁生存期的早期(例如,在 Page_Load 時)調用新的 Page.AddOnPreRenderCompleteAsync 方法來注冊一個 Begin 方法和一個 End 方法,如以下代碼所示:

            AddOnPreRenderCompleteAsync (
                        new BeginEventHandler(MyBeginMethod),
                        new EndEventHandler (MyEndMethod)
                        );

              接下來的操作比較有趣。該頁經歷其常規處理生命周期,直到 PreRender 事件剛剛引發之后。然后,ASP.NET 調用使用 AddOnPreRenderCompleteAsync 注冊的 Begin 方法。Begin 方法的任務是啟動諸如數據庫查詢或 Web 服務調用的異步操作,并立即返回。此時,分配給該請求的線程返回到線程池。此外,Begin 方法返回 IAsyncResult,它允許 ASP.NET 確定異步操作完成的時間,這個時候 ASP.NET 從線程池提取線程并調用 End 方法。當 End 返回之后,ASP.NET 執行該頁生命周期其余的部分,包括呈現階段。在 Begin 返回以及調用 End 之間,該請求處理線程可以自由地服務于其他請求,直至調用 End 且延遲呈現為止。由于 2.0 版的 .NET Framework 提供多種執行異步操作的方式,因此,您甚至無需實現 IasyncResult。反之,Framework 替您實現。

              圖 1 中的代碼隱藏類提供一個示例。響應頁包含一個 ID 為“Output”的 Label 控件。該頁使用 System.Net.HttpWebRequest 類提取 http://MSDN.microsoft.com 的內容。然后,它分析返回的 HTML,并將它發現的全部 HREF 目標列表寫出到 Label 控件。

            圖1

            using System;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;
            using System.Net;
            using System.IO;
            using System.Text;
            using System.Text.RegularExpressions;

            public partial class AsyncPage : System.Web.UI.Page
            {
             private WebRequest _request;

             void Page_Load (object sender, EventArgs e)
             {
              AddOnPreRenderCompleteAsync (
               new BeginEventHandler(BeginAsyncOperation),
               new EndEventHandler (EndAsyncOperation)
              );
             }

             IAsyncResult BeginAsyncOperation (object sender, EventArgs e,
            AsyncCallback cb, object state)
             {
              _request = WebRequest.Create("http://msdn.microsoft.com");
              return _request.BeginGetResponse (cb, state);
             }
             void EndAsyncOperation (IAsyncResult ar)
             {
              string text;
              using (WebResponse response = _request.EndGetResponse(ar))
              {
               using (StreamReader reader = new StreamReader(response.GetResponseStream()))
               {
                text = reader.ReadToEnd();
               }
              }

              Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase);
              MatchCollection matches = regex.Matches(text);

              StringBuilder builder = new StringBuilder(1024);
              foreach (Match match in matches)
              {
               builder.Append (match.Groups[1]);
               builder.Append("<br/>");
              }

              Output.Text = builder.ToString ();
             }
            }

             


              由于 HTTP 請求需要較長時間才能返回,因此,AsyncPage.aspx.cs 異步執行對它的處理。它在 Page_Load 中注冊 Begin 和 End 方法,并且在 Begin 方法中,它調用 HttpWebRequest.BeginGetResponse 啟用一個異步 HTTP 請求。BeginAsyncOperation 將由 BeginGetResponse 返回的 IAsyncResult 返回到 ASP.NET,導致當 HTTP 請求完成時,ASP.NET 調用 EndAsyncOperation。EndAsyncOperation 進而分析該內容并將結果寫入 Label 控件,之后進行呈現,并且 HTTP 響應返回到瀏覽器。


            圖 2 同步和異步頁處理

              圖 2 說明 ASP.NET 2.0 同步和異步頁之間的區別。當請求同步頁時,ASP.NET 為該請求分配線程池中的一個線程,并在該線程上執行頁。如果該請求停止執行 I/O 操作,則掛起線程,直到完成操作,從而可以完成該頁的生命周期。相反,異步頁通常通過 PreRender 事件執行。然后,調用使用 AddOnPreRenderCompleteAsync 注冊的 Begin 方法,之后,該請求處理線程返回線程池。Begin 啟動一個異步 I/O 操作,當該操作完成時,ASP.NET 從線程池提取另一個線程并調用 End 方法,并且在該線程上執行該頁生命周期的其余部分。


            圖 3 跟蹤輸出顯示異步頁的異步點

              對 Begin 的調用標記該頁的“異步點”。圖 3 中的跟蹤準確顯示異步點發生在何處。如果調用,則必須在異步點之前調用 AddOnPreRenderCompleteAsync — 即,不晚于該頁的 PreRender 事件。

              異步數據綁定

              通常情況下,ASP.NET 頁并不使用 HttpWebRequest 直接請求其他頁,但它們通常查詢數據庫并對結果進行數據綁定。因此,您將如何使用異步頁執行異步數據綁定呢?圖 4 中的代碼隱藏類顯示進行此操作的一種方式。

            using System;
            using System.Data;
            using System.Data.SqlClient;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;
            using System.Web.Configuration;

            public partial class AsyncDataBind : System.Web.UI.Page
            {
             private SqlConnection _connection;
             private SqlCommand _command;
             private SqlDataReader _reader;

             protected void Page_Load(object sender, EventArgs e)
             {
              if (!IsPostBack)
              {
               // Hook PreRenderComplete event for data binding
               this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);

               // Register async methods
               AddOnPreRenderCompleteAsync(
                new BeginEventHandler(BeginAsyncOperation),
                new EndEventHandler(EndAsyncOperation)
               );
              }
             }
             IAsyncResult BeginAsyncOperation (object sender, EventArgs e, AsyncCallback cb, object state)
             {
              string connect = WebConfigurationManager.ConnectionStrings
            ["PubsConnectionString"].ConnectionString;
              _connection = new SqlConnection(connect);
              _connection.Open();
              _command = new SqlCommand("SELECT title_id, title, price FROM titles", _connection);
              return _command.BeginExecuteReader (cb, state);
             }

             void EndAsyncOperation(IAsyncResult ar)
             {
              _reader = _command.EndExecuteReader(ar);
             }

             protected void Page_PreRenderComplete(object sender, EventArgs e)
             {
              Output.DataSource = _reader;
              Output.DataBind();
             }

             public override void Dispose()
             {
              if (_connection != null) _connection.Close();
              base.Dispose();
             }
            }

             

              AsyncDataBind.aspx.cs 與 AsyncPage.aspx.cs 使用相同的 AddOnPreRenderCompleteAsync 模式。但是,AsyncDataBind.aspx.cs 的 BeginAsyncOperation 方法調用 ADO.NET 2.0 中的新方法 SqlCommand.BeginExecuteReader(而非 HttpWebRequest.BeginGetResponse),以執行一個異步數據庫查詢。當調用完成時,EndAsyncOperation 調用 SqlCommand.EndExecuteReader 以獲取 SqlDataReader,然后將其存儲在私有字段中。在用于 PreRenderComplete 事件(在異步操作完成但呈現該頁之前引發)的事件處理程序中,AsyncDataBind.aspx.cs 之后將 SqlDataReader 綁定到 Output GridView 控件。從外觀上看,該頁類似于使用 GridView 呈現數據庫查詢結果的普通(同步)頁。但是在內部,該頁更具可伸縮性,因為它并不掛起線程池線程以等待查詢返回。

              異步調用 Web 服務

              另一個通常由 ASP.NET Web 頁執行的、與 I/O 相關的任務是調出 Web 服務。由于 Web 服務調用花費較長時間才能返回,因此,執行它們的頁是用于異步處理的理想選擇。

            圖5

            using System;
            using System.Data;
            using System.Configuration;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;

            public partial class AsyncWSInvoke1 : System.Web.UI.Page
            {
             private WS.PubsWebService _ws;
             private DataSet _ds;

             protected void Page_Load(object sender, EventArgs e)
             {
              if (!IsPostBack)
              {
               // Hook PreRenderComplete event for data binding
               this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);

               // Register async methods
               AddOnPreRenderCompleteAsync(new BeginEventHandler(BeginAsyncOperation),
            new EndEventHandler(EndAsyncOperation)
               );
              }
             }

             IAsyncResult BeginAsyncOperation (object sender, EventArgs e, AsyncCallback cb, object state)
             {
              _ws = new WS.PubsWebService();
              // Fix up URL for call to local VWD-hosted Web service
              _ws.Url = new Uri(Request.Url, "Pubs.asmx").ToString();
              _ws.UseDefaultCredentials = true;
              return _ws.BeginGetTitles (cb, state);
             }

             void EndAsyncOperation(IAsyncResult ar)
             {
              _ds = _ws.EndGetTitles(ar);
             }

             protected void Page_PreRenderComplete(object sender, EventArgs e)
             {
              Output.DataSource = _ds;
              Output.DataBind();
             }

             public override void Dispose()
             {
              if (_ws != null) _ws.Dispose();
               base.Dispose();
             }
            }

             

              圖 5 顯示生成調出 Web 服務的異步頁的方式。它使用圖 1 和 圖 4 中相同的 AddOnPreRenderCompleteAsync 機制。該頁的 Begin 方法通過調用 Web 服務代理的異步 Begin 方法啟動一個異步 Web 服務調用。該頁的 End 方法在私有字段中緩存對 Web 方法返回的 DataSet 的引用,并且 PreRenderComplete 處理程序將 DataSet 綁定到 GridView。作為參考,該調用的目標 Web 方法如以下代碼所示:

            [WebMethod]
                        public DataSet GetTitles ()
                        {
                        string connect = WebConfigurationManager.ConnectionStrings
                        ["PubsConnectionString"].ConnectionString;
                        SqlDataAdapter adapter = new SqlDataAdapter
                        ("SELECT title_id, title, price FROM titles", connect);
                        DataSet ds = new DataSet();
                        adapter.Fill(ds);
                        return ds;
                        }

              這只是其中一種方式,但并不是唯一的方式。.NET Framework 2.0 Web 服務代理支持兩種對 Web 服務進行異步調用的機制。一個是 .NET Framework 1.x 和 2.0 Web 服務代理中的每方法 Begin 和 End 方法。另一個是僅由 .NET Framework 2.0 的 Web 服務代理提供的新 MethodAsync 方法和 MethodCompleted 事件。

              如果一個 Web 服務有一個名為 Foo 的方法,那么除了具有名為 Foo、BeginFoo 和 EndFoo 的方法外,.NET Framework 版本 2.0 Web 服務代理還包括名為 FooAsync 的方法和名為 FooCompleted 的事件。可以通過注冊 FooCompleted 事件的處理程序并調用 FooAsync 來異步調用 Foo,如下所示:

            proxy.FooCompleted += new FooCompletedEventHandler (OnFooCompleted);
                        proxy.FooAsync (...);
                        ...
                        void OnFooCompleted (Object source, FooCompletedEventArgs e)
                        {
                        // Called when Foo completes
                        }
                        

              當異步調用由于 FooAsync 完成而開始時,將引發 FooCompleted 事件,從而導致調用 FooCompleted 事件處理程序。包裝該事件處理程序 (FooCompletedEventHandler) 的委托和傳遞給它的第二個參數 (FooCompletedEventArgs) 都隨 Web 服務代理一起生成。可通過 FooCompletedEventArgs.Result 訪問 Foo 的返回值。

              圖 6 展示使用 MethodAsync 模式異步調用 Web 服務的 GetTitles 方法的代碼隱藏類。從功能上講,該頁等同于圖 5 中的頁。但其內部實現則大為不同。AsyncWSInvoke2.aspx 包括一個 @ Page Async=“true” 指令,類似于 AsyncWSInvoke1.aspx。但是,AsyncWSInvoke2.aspx.cs 并不調用 AddOnPreRenderCompleteAsync;它注冊一個用于 GetTitlesCompleted 事件的處理程序,并調用 Web 服務代理上的 GetTitlesAsync。ASP.NET 仍然延遲呈現該頁,直到 GetTitlesAsync 完成。在內部,當異步調用開始以及完成時,它使用 System.Threading.SynchronizationContext 的一個實例(2.0 的一個新類)接收通知。

            圖6

            using System;
            using System.Data;
            using System.Configuration;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;

            public partial class AsyncWSInvoke2 : System.Web.UI.Page
            {
            private WS.PubsWebService _ws;
            private DataSet _ds;

            protected void Page_Load(object sender, EventArgs e)
            {
             if (!IsPostBack)
             {
              // Hook PreRenderComplete event for data binding
              this.PreRenderComplete += new EventHandler(Page_PreRenderComplete);

              // Call the Web service asynchronously
              _ws = new WS.PubsWebService();
              _ws.GetTitlesCompleted += new
              WS.GetTitlesCompletedEventHandler(GetTitlesCompleted);
              _ws.Url = new Uri(Request.Url, "Pubs.asmx").ToString();
              _ws.UseDefaultCredentials = true;
              _ws.GetTitlesAsync();
             }
            }

            void GetTitlesCompleted(Object source,
            WS.GetTitlesCompletedEventArgs e)
            {
             _ds = e.Result;
            }

            protected void Page_PreRenderComplete(object sender, EventArgs e)
            {
             Output.DataSource = _ds;
             Output.DataBind();
            }

            public override void Dispose()
            {
             if (_ws != null) _ws.Dispose();
             base.Dispose();
            }
            }

             


              使用 MethodAsync 而非 AddOnPreRenderCompleteAsync 實現異步頁有兩個優勢。首先,MethodAsync 將模擬、區域性和 HttpContext.Current 注入 MethodCompleted 事件處理程序,而 AddOnPreRenderCompleteAsync 則不然。其次,如果該頁進行多個異步調用,而且必須延遲呈現直到所有調用完成,則使用 AddOnPreRenderCompleteAsync 要求您生成一個在所有調用完成前保持無信號狀態的 IasyncResult。使用 MethodAsync,這樣的操作就不是必需的;您只需放置這些調用(數量不限),ASP.NET 引擎延遲該呈現階段,直到最后一個調用返回。


              異步任務

              MethodAsync 是從異步頁進行多個異步 Web 服務調用并延遲呈現階段直到所有調用完成的一個簡便方法。但如果您想在一個異步頁中執行若干異步 I/O 操作,而且這些操作不涉及 Web 服務,那該如何呢? 這么說,可以反過來生成一個 IAsyncResult,它可以返回到 ASP.NET 以允許它了解最后一個調用何時完成的嗎? 幸運的是,答案是否定的。

              在 ASP.NET 2.0 中,System.Web.UI.Page 類引入了另一個方法來簡化異步操作: RegisterAsyncTask。RegisterAsyncTask 比 AddOnPreRenderCompleteAsync 具有四個優勢。首先,除了 Begin 和 End 方法,RegisterAsyncTask 還允許您注冊當異步操作長時間無法完成時調用的超時方法。您可以通過在該頁的 @ Page 指令中包含 AsyncTimeout 屬性以聲明性方式設置超時。AsyncTimeout="5" 將超時設置為 5 秒。第二個優勢是,您可以在一個請求中多次調用 RegisterAsyncTask 來注冊若干異步操作。和使用 MethodAsync 一樣,ASP.NET 延遲呈現該頁,直到所有操作完成。第三,您可以使用 RegisterAsyncTask 的第四個參數將狀態傳遞給 Begin 方法。最后,RegisterAsyncTask 將模擬、區域性和 HttpContext.Current 注入 End 和 Timeout 方法。正如本文前面提到的,使用 AddOnPreRenderCompleteAsync 注冊的 End 方法的情況則不然。

              在其他方面,依賴于 RegisterAsyncTask 的異步頁與依賴于 AddOnPreRenderCompleteAsync 的異步頁相類似。它仍然需要 @ Page 指令(或等效的編程指令,它會將該頁的 AsyncMode 屬性設置為 true)中的 Async=“true” 屬性,而且它仍然與平時一樣通過 PreRender 事件執行,此時調用使用 RegisterAsyncTask 注冊的 Begin 方法,而且進一步保持請求處理直到最后一個操作完成。例如,圖 7 中的代碼隱藏類在功能上與圖 1 中的等效,但是它使用 RegisterTaskAsync 而非使用 AddOnPreRenderCompleteAsync。請注意名為 TimeoutAsyncOperation 的超時處理程序,如果 HttpWebRequest.BeginGetRequest 長時間無法完成,將調用該處理程序。相應的 .aspx 文件包括一個將超時間隔設置為 5 秒的 AsyncTimeout 屬性。還請注意傳給 RegisterAsyncTask 的第四個參數(可用于將數據傳送到 Begin 方法)的 null。

            圖7

            using System;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;
            using System.Net;
            using System.IO;
            using System.Text;
            using System.Text.RegularExpressions;

            public partial class AsyncPageTask : System.Web.UI.Page
            {
             private WebRequest _request;

             protected void Page_Load(object sender, EventArgs e)
             {
              PageAsyncTask task = new PageAsyncTask(
               new BeginEventHandler(BeginAsyncOperation),
               new EndEventHandler(EndAsyncOperation),
               new EndEventHandler(TimeoutAsyncOperation),
              null
             );
             RegisterAsyncTask(task);
             }

             IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
            AsyncCallback cb, object state)
             {
              _request = WebRequest.Create("http://msdn.microsoft.com");
              return _request.BeginGetResponse(cb, state);
             }

             void EndAsyncOperation(IAsyncResult ar)
             {
              string text;
              using (WebResponse response = _request.EndGetResponse(ar))
              {
               using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                 text = reader.ReadToEnd();
                }
              }

              Regex regex = new Regex("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase);
              MatchCollection matches = regex.Matches(text);

              StringBuilder builder = new StringBuilder(1024);
              foreach (Match match in matches)
              {
               builder.Append(match.Groups[1]);
               builder.Append("<br/>");
              }

              Output.Text = builder.ToString();
             }

             void TimeoutAsyncOperation(IAsyncResult ar)
             {
              Output.Text = "Data temporarily unavailable";
             }
            }


              RegisterAsyncTask 的主要優勢在于,它允許異步頁引發多個異步調用,并延遲呈現直到所有調用完成。它也很好地適用于單個異步調用,而且它提供了 AddOnPreRenderCompleteAsync 不具有的超時選項。如果生成一個只進行一個異步調用的異步頁,您可以使用 AddOnPreRenderCompleteAsync 或 RegisterAsyncTask。但對于放置兩個以上異步調用的異步頁,RegisterAsyncTask 極大地簡化了您的操作。

              由于超時值是每頁而非每調用設置,因此您可能想知道是否能改變單個調用的超時值。簡單的回答是否。您可以通過以編程方式修改頁的 AsyncTimeout 屬性,逐個請求地更改超時,但是您無法將不同超時分配給從同一請求初始化的不同調用。
            包裝它

              現在,您已經了解了 ASP.NET 2.0 中異步頁的實質。它們在即將推出的 ASP.NET 版本中非常易于實現,并且其體系結構允許您在一個請求中批處理多個異步 I/O 操作,并延遲該頁的呈現直到所有操作完成。通過與異步 ADO.NET 和 .NET Framework 中的其他新異步功能相結合,異步 ASP.NET 頁針對因充滿線程池而限制可伸縮性的 I/O 綁定請求問題提供了解決方案。

              當生成異步頁時最后需要注意的一點是,不應該啟動來自 ASP.NET 使用的同一線程池的異步操作。例如,在頁的異步點調用 ThreadPool.QueueUserWorkItem 會起反作用,因為該方法來自線程池,從而導致純粹獲取用于處理請求的零線程。相反,調用內置于 Framework 中的異步方法(例如,方法 HttpWebRequest.BeginGetResponse 和 SqlCommand.BeginExecuteReader)通常認為是安全的,因為這些方法傾向于使用完成端口實現異步行為。

            posted on 2007-09-18 16:04 獨孤九劍 閱讀(143) 評論(0)  編輯 收藏 引用 所屬分類: Learn articles
            国内精品人妻无码久久久影院导航| 成人久久免费网站| 久久精品www人人爽人人| 久久天堂AV综合合色蜜桃网| 亚洲va久久久噜噜噜久久狠狠| 国内精品久久国产| 无码专区久久综合久中文字幕| 精品国产乱码久久久久久人妻| 国产午夜免费高清久久影院| 久久精品无码免费不卡| 少妇人妻综合久久中文字幕| 久久精品亚洲一区二区三区浴池| 久久精品中文字幕久久| 久久久国产99久久国产一| 久久精品夜夜夜夜夜久久| 久久e热在这里只有国产中文精品99| 伊色综合久久之综合久久| 国内精品久久久久影院日本| 久久精品一区二区影院| 久久国产精品成人片免费| 亚洲精品tv久久久久久久久久| 国产精品久久精品| 久久久久久国产精品免费无码 | 国产毛片欧美毛片久久久| 国产精品视频久久久| 99久久综合国产精品免费| 伊人久久大香线蕉影院95| 久久人爽人人爽人人片AV| 伊人久久大香线蕉精品不卡| 国产综合精品久久亚洲| 99久久久国产精品免费无卡顿| 无码人妻久久一区二区三区蜜桃 | 亚洲国产高清精品线久久| 91久久婷婷国产综合精品青草| 一本色道久久HEZYO无码| 中文成人久久久久影院免费观看 | 九九久久精品无码专区| 国内精品久久国产大陆| 国内精品久久久久久野外| 2021久久国自产拍精品| 久久99精品综合国产首页|