ASP.NET - 多线程



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

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

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

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

创建线程

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

ThreadStart childthreat = new ThreadStart(childthreadcall);

线程生命周期

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

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

  • 未启动状态:这是创建线程实例但未调用 Start 方法的情况。

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

  • 不可运行状态:线程不可运行,当

    • 已调用 Sleep 方法
    • 已调用 Wait 方法
    • 被 I/O 操作阻塞
  • 死亡状态:这是线程已完成执行或已被中止的情况。

线程优先级

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

优先级可以分为:

  • 高于正常
  • 低于正常
  • 最高
  • 最低
  • 正常

创建线程后,使用线程类的 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 取消对当前线程请求的 Abort。
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 Thread
广告