ASP.NET中的多线程整理

   线程,是操作系统中的术语,是操作系统进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以有很多线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。我们把用来执行用户任务的线程称为工作线程。而线程池,是一种成熟的线程使用模式。

  为什么要创建线程池?

  线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么,线程池的最重要的特征也就是最大程度利用线程。所以线程池的目的就是为了减少创建和切换线程的额外开销,利用已经的线程多次循环执行多个任务从而提高系统的处理能力.

  在ASP.NET工作进程中有两种线程池,Worker线程池处理所有传入的请求, I / O线程池处理的I / O(访问文件系统,Web服务和数据库等)。每个应用程序域都有其自己的线程池,可以排队到线程池的操作的数量只受可用内存的限制,然而,对线程池中的线程数的限制在这个过程中可以同时被激活。

ASP.NET中的多线程整理_第1张图片

  当我们发出一个(异步)页面请求。一个Worker线程就会被从Worker线程池中取出, 这个Worker线程会触发I/O操作。当I/O操作开始时,另一个线程将会被从I/O线程池中取出,在收到I/O线程的返回值之前,Worker线程会一直处于闲置状态。所以,如果你的页面加载事件触发了多个I/O操作,那么,Worker线程就很可能会被闲置很长时间。而线程池能够把正处于闲置状态的Worker线程回收,使他能够为其他的页面请求提供服务。从而,降低系统开销。

ASP.NET中的多线程整理_第2张图片

只要并发请求的数量不超过线程池的可用线程的数量,一切都很好。但是,当你正在构建企业级应用程序时,你的线程池中的线程的数量就会达到极限。当这种情况发生时,新的请求会进入请求队列。 而ASP.NET支持在它开始拒绝请求并返回错误503服务不可用之前的最大请求数量,但是,我还没搞清楚这个具体的数量值是多少。

以下为多线程示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.Threading;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace AsyncWeb
{
    public partial class ThreadAsync : System.Web.UI.Page
    {
        private string connect = ConfigurationManager.ConnectionStrings["ValvesTestConnectionString"].ConnectionString.ToString();

        private DataTable threadOneResult;
        private DataTable threadTwoResult;
        private DataTable threadThreeResult;

        protected void Page_Load(object sender, EventArgs e)
        {
            AsyncCallsWithThreadPool();
        }

        public void AsyncCallsWithThreadPool()
        {
            ThreadPool.QueueUserWorkItem(ThreadOne, 1000);
            ThreadPool.QueueUserWorkItem(ThreadTwo, 5000);
            ThreadPool.QueueUserWorkItem(ThreadThree, 10000);

            while (threadOneResult == null ||
                threadTwoResult == null ||
                threadThreeResult == null)
            {
                Thread.Sleep(10000);
            }

            // continue
            threadOneResult.Merge(threadTwoResult);
            threadOneResult.Merge(threadThreeResult);
            Output.DataSource = threadOneResult;
            Output.DataBind();
        }

        private void ThreadOne(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 10 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadOneResult = ds.Tables[0];
        }

        private void ThreadTwo(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 20 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadTwoResult = ds.Tables[0];
        }

        private void ThreadThree(object state)
        {
            DataSet ds = new DataSet();
            using (SqlConnection conn = new SqlConnection(connect))
            {
                SqlCommand cmd = new SqlCommand("SELECT top 30 header_id, line_id, ordered_item FROM oe_order_lines_all", conn);
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                da.Fill(ds);
            }
            threadThreeResult = ds.Tables[0];
        }
    }
}

在这个例子中,我在线程池中放了三个线程。当程序启动时,这三个线程就会被从线程池中取出,然后分别执行对应的操作,如从数据库中取数据。这三个线程在执行时,主线程会处于闲置状态,直到这三个线程完成主线程才会继续执行。我这里是使用While循环来实现的,其实还可以用其他方式实现。使用这种方式对线程的控制程度比较高,要特别小心的使用。ASP.NET也为开发人员提供了它自己的异步编程模型,一起来看看吧!

 

ASP.NET 中的异步编程模型

ASP.NET提供的异步编程模型包含:异步页(Asynchronous Page),Asynchronous Module 和 Asynchronous WebService。使用Asynchronous WebService实现异步的方式比较普遍,技术文档比较多。所以,本篇就不对Asynchronous WebService进行介绍。好了,开始吧!

异步页(Asynchronous Page)

ASP.NET中的多线程整理_第3张图片

  上图为普通页面和异步页面的生命周期事件。相对于普通的页面生存周期事件,异步页多了异步事件,上图绿色的部分就是异步事件在页面事件中的执行位置。我们可以很清楚的看到主线程被一分为二了,其实这里主线程还是在的,只不过被闲置了来等待异步线程的执行结束。开发人员可以通过在ASP.NET中注册(Begin,End)方法来实现异步操作。ASP.NET会调用Begin方法,在begin方法中去执行I/O操作,如执行数据库查询操作,ASP.NET将立即从Begin方法中返回而不会去等待返回值。ASP.NET只要检测到线程返回了,就会立即把这个线程放回到线程池中,然后,使用这个线程去处理其他的请求。

  从Begin方法中返回的是一个IAsyncResult 接口,正是通过这个接口ASP.NET才知道Begin方法什么时候结束。当Begin方法结束时,ASP.NET会到线程池中取出另一个线程去调用end方法。在end方法中,我们可以对返回值进行处理。

  从ASP.NET的角度看,这只是一个普通的请求,但是确由两个线程来执行。这会不会有什么问题呢?

  创建异步页的步骤很简单,首先在.aspx文件加入Async=”True”,目的就是程序在Runtime时告诉ASP.NET这是个异步页。如果是处理数据的异步请求,要在Connection String 中加“Asynchronous Processing=true;”。

如下示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

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

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

            RegisterAsyncTask(task); 
        }
        IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
        AsyncCallback cb, object state)
        {
            string connect = ConfigurationManager.ConnectionStrings["ValvesTestConnectionString"].ConnectionString.ToString();
            _connection = new SqlConnection(connect);

            _connection.Open();
            _command = new SqlCommand(
                "SELECT top 10 header_id, line_id, ordered_item FROM oe_order_lines_all", _connection);
            return _command.BeginExecuteReader(cb, state);
        }

        void EndAsyncOperation(IAsyncResult ar)
        {
            _reader = _command.EndExecuteReader(ar);
            DataTable dt = new DataTable();
            dt.Load(_reader);
            Output.DataSource = dt;
            Output.DataBind();
        }

        void TimeoutAsyncOperation(IAsyncResult ar)
        {

            // Called if async operation times out (@ Page AsyncTimeout)

            Label1.Text = "Data temporarily unavailable";

        }    
    }
    
}

这是一个比较简单的例子。但是,ASP.NET实现异步页的步骤还是比较复杂的,至少比普通的页复杂很多。但是,从性能上考虑,新请求能够很快进入管道,并且不需要在应用程序请求队列中等待很长的时间,这样就使得整个应用程序的性能都得到了提高。

 

ASP.NET异步HttpHandlers

ASP.NET中第二种异步模式是使用HttpHandler, httphander通过文件的后缀名来确定请求类型然后再确定使用哪个处理方法。ASPX handler 用来处理来自.aspx页面的请求。, 简单的说,其实它就是一个实现了IHttpHandler接口的类,它包括IsReusable方法和ProcessRequest方法。ProcessRequest方法的主要工作就是处理HttpRequest并把它转化为HttpResponse, 再将HttpContext传到page中。

// Name this C# file HandlerTest.cs and compile it with the
// command line: csc /t:library /r:System.Web.dll HandlerTest.cs.
// Copy HandlerTest.dll to your \bin directory.

using System.Web;

namespace HandlerExample
{
   public class MyHttpHandler : IHttpHandler
   {
      // Override the ProcessRequest method.
      public void ProcessRequest(HttpContext context)
      {
         context.Response.Write("

This is an HttpHandler Test.

"
); context.Response.Write("

Your Browser:

"
); context.Response.Write("Type: " + context.Request.Browser.Type + "
"
); context.Response.Write("Version: " + context.Request.Browser.Version); } // Override the IsReusable property. public bool IsReusable { get { return true; } } } } /* ______________________________________________________________ To use this handler, include the following lines in a Web.config file. */


开发人员可以使用IHttpAsyncHandler来实现异步的HttpHandler。待续。。。。

转载于:https://www.cnblogs.com/Dannier/archive/2012/05/31/Thread.html

你可能感兴趣的:(ASP.NET中的多线程整理)