ASP.NET - 多线程

线程被定义为程序的执行路径。 每个线程定义一个独特的控制流。 如果您的应用程序涉及复杂且耗时的操作,例如数据库访问或一些密集的 I/O 操作,那么设置不同的执行路径或线程,每个线程执行特定的作业通常会很有帮助。

线程是轻量级进程。 使用线程的一个常见示例是现代操作系统实现并发编程。 使用线程可以节省 CPU 周期的浪费并提高应用程序的效率。

到目前为止,我们编译的程序中单个线程作为单个进程运行,该进程是应用程序的运行实例。 但是,通过这种方式,应用程序一次可以执行一项作业。 为了使其一次执行多个任务,可以将其划分为更小的线程。

在.Net中,线程是通过"System.Threading"命名空间处理的。 创建System.Threading.Thread类型的变量允许您创建一个新线程来开始工作。 它允许您创建和访问程序中的各个线程。

创建线程

通过创建 Thread 对象并为其构造函数提供 ThreadStart 引用来创建线程。

ThreadStart childthreat = new ThreadStart(childthreadcall);

线程生命周期

线程的生命周期从 System.Threading.Thread 类的对象创建时开始,到线程终止或完成执行时结束。

以下是线程生命周期中的各种状态:

  • 未启动状态:线程实例创建完成但Start方法没有被调用的情况。

  • 就绪状态:线程准备好执行并等待CPU周期的情况。

  • 不可运行状态:当以下情况时,线程不可运行:

    • 已调用睡眠方法
    • 已调用等待方法
    • 被 I/O 操作阻止
  • 死亡状态:线程已完成执行或已中止的情况。

线程优先级

Thread 类的 Priority 属性指定一个线程相对于其他线程的优先级。 .Net运行时选择具有最高优先级的就绪线程。

优先事项可分为以下几类:

  • Above normal
  • Below normal
  • Highest
  • Lowest
  • Normal

创建线程后,将使用线程类的 Priority 属性设置其优先级。

NewThread.Priority = ThreadPriority.Highest;

线程属性和方法

Thread 类具有以下重要属性:

属性 描述
CurrentContext 获取线程正在执行的当前上下文。
CurrentCulture 获取或设置当前线程的区域性。
CurrentPrinciple 获取或设置线程的当前主体以实现基于角色的安全性。
CurrentThread 获取当前正在运行的线程。
CurrentUICulture 获取或设置资源管理器使用的当前区域性,以在运行时查找特定于区域性的资源。
ExecutionContext 获取一个 ExecutionContext 对象,其中包含有关当前线程的各种上下文的信息。
IsAlive 获取表示当前线程执行状态的值。
IsBackground 获取或设置一个值,指示线程是否为后台线程。
IsThreadPoolThread 获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId 获取当前托管线程的唯一标识符。
Name 获取或设置线程的名称。
Priority 获取或设置一个指示线程调度优先级的值。
ThreadState 获取包含当前线程状态的值。

Thread 类有以下重要方法:

方法 描述
Abort 在调用它的线程中引发 ThreadAbortException,以开始终止线程的过程。 调用该方法通常会终止线程。
AllocateDataSlot 在所有线程上分配未命名的数据槽。 为了获得更好的性能,请改用使用 ThreadStaticAttribute 属性标记的字段。
AllocateNamedDataSlot 在所有线程上分配一个命名数据槽。 为了获得更好的性能,请改用使用 ThreadStaticAttribute 属性标记的字段。
BeginCriticalRegion 通知主机执行即将进入一个代码区域,其中线程中止或未处理的异常的影响可能会危及应用程序域中的其他任务。
BeginThreadAffinity 通知主机托管代码即将执行依赖于当前物理操作系统线程标识的指令。
EndCriticalRegion 通知主机执行即将进入代码区域,其中线程中止或未处理异常的影响仅限于当前任务。
EndThreadAffinity 通知主机托管代码已完成执行依赖于当前物理操作系统线程标识的指令。
FreeNamedDataSlot 消除进程中所有线程的名称和槽之间的关联。 为了获得更好的性能,请改用使用 ThreadStaticAttribute 属性标记的字段。
GetData 从当前线程的当前域内的当前线程上的指定槽中检索值。 为了获得更好的性能,请改用使用 ThreadStaticAttribute 属性标记的字段。
GetDomain 返回当前线程运行的当前域。
GetDomainID 返回唯一的应用程序域标识符。
GetNamedDataSlot 查找指定的数据槽。 为了获得更好的性能,请改用使用 ThreadStaticAttribute 属性标记的字段。
Interrupt 中断处于WaitSleepJoin线程状态的线程。
Join 阻塞调用线程直到线程终止,同时继续执行标准 COM 和 SendMessage 泵送。 该方法有不同的重载形式。
MemoryBarrier 按如下方式同步内存访问:执行当前线程的处理器无法以这样的方式重新排序指令:调用 MemoryBarrier 之前的内存访问在调用 MemoryBarrier 之后的内存访问之后执行。
ResetAbort 取消当前线程请求的中止。
SetData 为当前正在运行的线程的当前域设置指定槽中的数据。 为了获得更好的性能,请改用标有 ThreadStaticAttribute 属性的字段。
Start 启动一个线程。
Sleep 使线程暂停一段时间。
SpinWait 使线程等待迭代参数定义的次数。
VolatileRead() 读取字段的值。 该值是计算机中任何处理器最新写入的值,无论处理器的数量或处理器缓存的状态如何。 该方法有不同的重载形式。
VolatileWrite() 立即将值写入字段,以便该值对计算机中的所有处理器都可见。 该方法有不同的重载形式。
Yield 导致调用线程将执行让步给准备在当前处理器上运行的另一个线程。 操作系统选择要屈服的线程。

示例

以下示例说明了 Thread 类的用法。 该页面有一个标签控件,用于显示来自子线程的消息。 来自主程序的消息使用 Response.Write() 方法直接显示。 因此它们出现在页面顶部。

源文件如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="threaddemo._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

   <head runat="server">
      <title>
         Untitled Page
      </title>
   </head>
   
   <body>
      <form id="form1" runat="server">
         <div>
            <h3>Thread Example</h3>
         </div>
         
         <asp:Label ID="lblmessage" runat="server" Text="Label">
         </asp:Label>
      </form>
   </body>
   
</html>

文件背后的代码如下:

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;

using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

using System.Xml.Linq;
using System.Threading;

namespace threaddemo
{
   public partial class _Default : System.Web.UI.Page
   {
      protected void Page_Load(object sender, EventArgs e)
      {
         ThreadStart childthreat = new ThreadStart(childthreadcall);
         Response.Write("Child Thread Started <br/>");
         Thread child = new Thread(childthreat);
         
         child.Start();
         
         Response.Write("Main sleeping  for 2 seconds.......<br/>");
         Thread.Sleep(2000);
         Response.Write("<br/>Main aborting child thread<br/>");
         
         child.Abort();
      }
      
      public void childthreadcall()
      {
         try{
            lblmessage.Text = "<br />Child thread started <br/>";
            lblmessage.Text += "Child Thread: Coiunting to 10";
            
            for( int i =0; i<10; i++)
            {
               Thread.Sleep(500);
               lblmessage.Text += "<br/> in Child thread </br>";
            }
            
            lblmessage.Text += "<br/> child thread finished";
            
         }catch(ThreadAbortException e){
         
            lblmessage.Text += "<br /> child thread - exception";
            
         }finally{
            lblmessage.Text += "<br /> child thread - unable to catch the  exception";
         }
      }
   }
}

请遵守以下规定

  • 当页面加载时,一个新线程将通过 childthreadcall() 方法的引用启动。 主线程活动直接显示在网页上。

  • 第二个线程运行并向标签控件发送消息。

  • 主线程休眠 2000 毫秒,在此期间子线程执行。

  • 子线程一直运行,直到被主线程中止。 它引发 ThreadAbortException 并终止。

  • 控制权返回到主线程。

执行时,程序会发送以下消息:

ASP.NET 线程