zoukankan      html  css  js  c++  java
  • ASP.NET中的多线程整理

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

      为什么要创建线程池?

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

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

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

    只要并发请求的数量不超过线程池的可用线程的数量,一切都很好。但是,当你正在构建企业级应用程序时,你的线程池中的线程的数量就会达到极限。当这种情况发生时,新的请求会进入请求队列。 而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中注册(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("<H1>This is an HttpHandler Test.</H1>");      
             context.Response.Write("<p>Your Browser:</p>");
             context.Response.Write("Type: " + context.Request.Browser.Type + "<br>");
             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.
    
    <configuration>
       <system.web>
          <httpHandlers>
             <add verb="*" path="handler.aspx" type="HandlerExample.MyHttpHandler,HandlerTest"/>
          </httpHandlers>
       </system.web>
    </configuration>
    */


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

  • 相关阅读:
    docker 容器启动初始化,centos镜像启动并执行
    odoo 分布式session 失效解决方案
    文件分布式存储 minio docker
    odoo reports 报表打印pdf初探
    odoo 分布式快速更新
    linux Warning: Stopping docker.service, but it can still be activated by:
    linux 查看80端口的连接数
    css flex 涨姿势了
    odoo 后台打印日志修改
    iOS 导航栏消失
  • 原文地址:https://www.cnblogs.com/Dannier/p/Thread.html
Copyright © 2011-2022 走看看